Skip to content
本页目录

2021

3.28

TS 枚举

  • ts 枚举写了注释后,在文件外面使用的地方也会有提示。(必须写在前面)

Jsonp 原理

  • 主要就是利用了 script 标签的src没有跨域限制来完成的。
js
<script type='text/javascript'>
    window.jsonpCallback = function (res) {
        console.log(res)
    }
</script>
<script src='http://localhost:8080/jsonp?id=1&cb=jsonpCallback' type='text/javascript'></script>
  • 注意的点就是回调函数的名称需要前后端确认好,如这里的 cb
js
const Koa = require('koa');
const app = new Koa();
const items = [
  { id: 1, title: 'title1' },
  { id: 2, title: 'title2' }
];

app.use(async (ctx, next) => {
  if (ctx.path === '/api/jsonp') {
    const { cb, id } = ctx.query;
    const title = items.find((item) => item.id == id)['title'];
    ctx.body = `${cb}(${JSON.stringify({ title })})`;
    return;
  }
});
app.listen(8080, () => {
  console.log('listen 8080...');
});

参考链接

4.2

JavaScript 里面的数组下标一定是数字吗?

  • 不一定,数组也是一个对象,可以通过 [].key 的形式来赋值。

4.7

react 中的 setState() 函数的第二个参数

- setState的第二个回调函数会在更新state,重新触发render后执行。

React 中的高阶组件(HOC)与 Render Props 的概念与对比。

Render Props 更灵活。

4.9

大图片的线性加载和渐进式加载

  • 线性加载其实就是在我们浏览网页时常看见的那种 —— 网速足够慢或者图片尺寸过大时,可以看到图片的加载方式由上至下,一点一点的加载出来。
  • 渐进式 则如同名字一般, 它会先显示低分辨率的近似图像,再逐步的增加图片分辨率(模糊到清晰)。

4.12

webpack 热更新原理

  • img 如上图所示,右侧 Server 端是 webpack-dev-server 启动本地服务,内部实现其实是用 express 起的服务。

  • 1、服务端和客户端使用的是 websocket 实现长连接。

  • 2、webpack 在本地开发用 webpack-dev-server 起服务同时开启了热更新之后, webpack 会监听源文件的更改。 - 当你保存文件时,webpack 会重新打包编译一次代码同时生成一个 hash 值、改动模块的 json 文件、改动模块的 js文件。 - 编译完成后通过 websocket 把当前编译后文件的 hash 值发给客户端。

  • 3、客户端收到推送过来的 hash 值,会和上次的 hash 值比较 - 一样的话会读取缓存 - 不一样则通过 ajaxjsonp 从服务器端获取最新的文件

  • 4、最后通过 内存文件系统 去替换有修改的内容,实现局部刷新。

参考——https://juejin.cn/post/6844903933157048333#heading-32

4.13

React JSX 怎么变成真实 DOM 的

  • 首先我们在 react 中写的 jsx/tsx 会通过 babel 被编译为 React.createElement()

Drawing 0.png

  • React.createElement() 到底做了什么?

    • 这个函数的入参如下:

      javascript
      export function createElement(type, config, children)
    • React.createElement() 有三个参数,这三个参数包括了创建一个元素所要知道的所有信息。

    • type:用来标识节点类型,可以是一些原生的 HTML 标签,也可以是 React 中的组件。

    • config:这个参数是以对象形式传入的,里面存放的是节点所有的属性。(key、className 等等)。

    • children 以对象形式传入,它记录的是组件标签之间嵌套的内容。(以 createElement() 的形式)。

    比如下 DOM 结构:

    html
    <ul className="list">
      <li key="1">1</li>
      <li key="2">2</li>
    </ul>

    对应的是如下形式的 createElement() 调用:

    js
    React.createElement(
      'ul',
      {
        // 传入属性键值对
        className: 'list'
        // 从第三个入参开始往后,传入的参数都是 children
      },
      React.createElement(
        'li',
        {
          key: '1'
        },
        '1'
      ),
      React.createElement(
        'li',
        {
          key: '2'
        },
        '2'
      )
    );
    • createElement 函数在对传进来的参数进行一顿处理之后,最后 return 了一个ReactElement 函数,并传入处理过后的参数。
  • 其实在 React.createElement 中没有十分复杂的涉及算法或真实 DOM 的逻辑,它的每一个步骤几乎都是在格式化数据。说得更直白点,createElement 就像是开发者和 ReactElement 调用之间的一个“转换器”、一个数据处理层。它可以从开发者处接受相对简单的参数,然后将这些参数按照 ReactElement 的预期做一层格式化,最终通过调用 ReactElement 来实现元素的创建。

    Drawing 5.png

typescript 中的枚举 enum 类型其实是数字 number 的子类型

  • number 类型可以理解为所有的数字,而枚举类型 enum 可以理解为有限个的数字。

4.14

关于 React 中的合成事件的事件池

  • 在 React 17 之前,合成事件对象 会被放进一个叫 事件池 的地方统一管理。
  • 这样做的目的是能够实现 事件对象 的复用,进而提高性能。
  • 但是这样处理的话,每当事件处理函数执行完毕之后,对应的合成事件对象就会被"格式化",为下一次复用做准备,这就意味着我们在事件处理函数执行完毕之后就拿不到事件对象了。如下这个例子(官方提供)
js
function handleChange(e) {
  // This won't work because the event object gets reused.
  setTimeout(() => {
    console.log(e.target.value); // Too late!
  }, 100);
}
  • 如果你在一个 DOM 元素上绑定上面这个事件处理函数,触发之后控制台会提示如下信息:
js
Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property `target` on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use `event.persist()`. See https://fb.me/react-event-pooling for more information.
  • 根据这个提示信息能够看到解决方法:要想拿到目标事件对象,必须显式地告诉 React——我永远需要它,也就是调用 e.persist() 函数,像下面这样:
js
function handleChange(e) {
  // This won't work because the event object gets reused.
  event.persist();
  setTimeout(() => {
    console.log(e.target.value); // Too late!
  }, 100);
}
  • React 17 拥抱了新时代的潮流,重新在研发体验和向下兼容性能之间做了选择,这一次,它选择了前者——放弃事件池,为每一个合成事件创建新的对象。因此在 React 17 中,我们不需要 e.persist(),也可以随时随地访问我们想要的事件对象。

4.15

ES6 字符串新增方法

includes()、startsWith()、 endsWith()

传统上,JavaScript 只有indexOf方法,可以用来****。ES6 又提供了三种新方法。

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
javascript
let s = 'Hello world!';

s.startsWith('Hello'); // true
s.endsWith('!'); // true
s.includes('o'); // true
  • 这三个方法都支持第二个参数,表示开始搜索的位置。
javascript
let s = 'Hello world!';

s.startsWith('world', 6); // true
s.endsWith('Hello', 5); // true
s.includes('Hello', 6); // false
  • 上面代码表示,使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。

repeat()

  • repeat方法返回一个新字符串,表示将原字符串重复n次。
  • 参数如果是小数,会被取整。
  • 如果参数是负数或者Infinity则会报错。
  • 但是,如果参数是 0 到-1 之间的小数,则等同于 0,这是因为会先进行取整运算。0 到-1 之间的小数,取整以后等于-0repeat视同为 0。参数NaN等同于 0。如果repeat的参数是字符串,则会先转换成数字。
javascript
'jing'.repeat(3); // "jingjingjing"
'hao'.repeat(2); // "haohao"
'bao'.repeat(0); // ""

'hao'.repeat(2.9); // "haohao"

'haha'.repeat(Infinity);
// RangeError
'haha'.repeat(-1);
// RangeError

padStart()、padEnd()

  • ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。
javascript
'x'.padStart(5, 'ab'); // 'ababx'
'x'.padStart(4, 'ab'); // 'abax'

'x'.padEnd(5, 'ab'); // 'xabab'
'x'.padEnd(4, 'ab'); // 'xaba'
  • 上面代码中,padStart()padEnd()一共接受两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。
  • 如果原字符串的长度,等于或大于最大长度,则字符串补全不生效,返回原字符串。
  • 如果用来补全的字符串与原字符串,两者的长度之和超过了最大长度,则会截去超出位数的补全字符串。
  • 如果省略第二个参数,默认使用空格补全长度。
  • padStart()的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。
javascript
'1'.padStart(10, '0'); // "0000000001"
'12'.padStart(10, '0'); // "0000000012"
'123456'.padStart(10, '0'); // "0000123456"

trimStart()、trimEnd()

ES2019 对字符串实例新增了trimStart()trimEnd()这两个方法。它们的行为与trim()一致,trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。

js
const s = '  abc  ';

s.trim(); // "abc"
s.trimStart(); // "abc  "
s.trimEnd(); // "  abc"
  • 上面代码中,trimStart()只消除头部的空格,保留尾部的空格。trimEnd()也是类似行为。
  • 除了空格键,这两个方法对字符串头部(或尾部)的 tab 键、换行符等不可见的空白符号也有效。
  • 浏览器还部署了额外的两个方法,trimLeft()trimStart()的别名,trimRight()trimEnd()的别名。

matchAll()

  • matchAll()方法返回一个正则表达式在当前字符串的所有匹配。

replaceAll

历史上,字符串的实例方法replace()只能替换第一个匹配。

javascript
'aabbcc'.replace('b', '_');
// 'aa_bcc'
  • 如果要替换所有的匹配,不得不使用正则表达式的g修饰符。
  • 正则表达式毕竟不是那么方便和直观,ES2021 引入了replaceAll()方法,可以一次性替换所有匹配。
javascript
'aabbcc'.replaceAll('b', '_');
// 'aa__cc'

详见阮一峰 ES6

4.27

对象中的 key

  • 任何对象作为对象的 key 的时候,都会被转成 '[object Object]'
  • 如果是函数作为对象的 key 的时候,会把整个函数字符串化作为 key

看下面代码:

js
var a = {};
var b = { jing: 'hao' };
var c = { test: 'jing' };
function jing() {
	var jing = 999;
}
a[b] = 'b';
a[c] = 'c';
a[jing] = 'jing';
console.log(a);

// 输出结果如下:
{
  '[object Object]': 'c',
  'function jing() {\n\tvar jing = 999;\n}': 'jing'
}

Symbol 类型作为 key

再看下面这段代码

js
var a = {};
var b = Symbol('123');
var c = Symbol('123');
a[b] = 'b';
a[c] = 'c';
console.log(a);
console.log(a[b]);
console.log(a[c]);
console.log(Object.keys(a));

// 对应输出结果分别如下:
// { [Symbol(123)]: 'b', [Symbol(123)]: 'c' }
// b
// c
// []

Symbol 定义的属性不会出现在下面循环中:

  • for in:可获取原型属性,不可获取不可枚举属性
  • for of:不可遍历对象,可遍历数组
  • Object.keys:原型属性和不可枚举属性都不能获取
  • Object.getOwnPropertyByNames:不可获取原型属性,可获取不可枚举属性
  • JSON.stringify:原型属性和不可枚举属性都不能获取
  • Reflect.ownKeys:可获取不可枚举和 Symbol,不可获取原型

所以上面 Object.keys(a) 的结果是 []

2021-7-3

Vue 项目中支持 less 定义全局的 css 变量

  • 基于 sass-resources-loader 包实现
js
npm install sass-resources-loader --save-dev
  • 然后在构建文件中添加 loader
js
{
  test: /\.less$/,
  use: [
      {
          loader: 'vue-style-loader'
      },
      {
          loader: 'css-loader'
      },
      {
          loader: 'less-loader'
      },
      {
          loader: 'sass-resources-loader',
          options: {
              resources: [path.resolve(__dirname, '../src/styles/variable.less')]
            //这里文件路径就是定义全局公共变量的文件
          }
      }
  ]
},

2021-7-5

span:nth-of-type(1)

  • 选中当前父类下的第几个子元素标签(括号内数字 1 就是第一个)

2021-7-26

vue key 的问题

  • 今天碰到一个 Vue 中 key 的问题。
  • 自定义列拖拽顺序后页面表格不会更新列顺序的问题。
  • 后来涛哥发现是 key 的问题,因为表格的 key 我用的是每列的 name ,在拖拽的时候虽然数据的顺序变化了,但是每一项的数据的 key 还是一样,所以 Vue 虚拟 DOM 自己优化了,不会更新页面。

2021-8-4

怎么解决 G2 图表数据更新了旧图表还在页面上造成多个重复的图表。

  • 图表根据条件查询到的数据渲染,查询后旧图表没清除,造成页面显示多个图表。

    js
    let chartCurrent=null//图表初始值为null
    paintChart=()=>{
    chartCurrent&&chartCurrent.destroy()//chartCurrent存在就销毁重新绘制chart
    let chart = new G2.Chart({
          container: 'circle',
          autoFit: true,
          height: 600,
          padding:'auto'
        });
    }
    chart.source(data);
    chart.interval().position('date*expected');
    chart.render();
    chartCurrent=chart
    https://blog.csdn.net/weixin_44471622/article/details/105221258

2021-9-20

1、Vue 中给组件上绑定原生事件

  • Vue 中给组件上绑定原生事件的时候一定要加上 .native
  • 没加的话会在组件内部找事件,加上了就是在当前组件内找定义事件。

2、去掉滚动条方法

css
scrollbar-width: none; /* firefox */
-ms-overflow-style: none; /* IE 10+ */
&::-webkit-scrollbar {
  display: none; /* Chrome Safari */
}

2021-9-25

设置表格 td 最大宽度,里面内容换行

  • 设置表格 td 最大宽度,里面内容换行。
  • 一般字母的话会被浏览器默认是一个字符串或者说一个单词,所以不会自动换行。
css
table-layout:fixed;
td {
  max-width: 200px;
  word-wrap:break-word;
}

2021-9-26

Vue 动态组件

  • Vue 动态组件使用(有需要可以加 keep-alive)
vue
<template>
  <div class="tidb-colonyInfo">
    <div class="top">
      <h3>集群:test</h3>
      <el-tag type="success" effect="dark">运行中</el-tag>
    </div>
    <div class="radio-button">
      <el-radio-group v-model="defaultComponent" @change="changeCom">
        <el-radio-button label="ColonyBaseInfo">基本信息</el-radio-button>
        <el-radio-button label="Monitor">监控</el-radio-button>
        <el-radio-button label="AlarmConf">报警配置</el-radio-button>
        <el-radio-button label="ParameterConf">参数设置</el-radio-button>
        <el-radio-button label="OperationRecord">操作记录</el-radio-button>
      </el-radio-group>
    </div>
    <component :is="viewCom"></component>
  </div>
</template>

<script>
import ColonyBaseInfo from '@/package/tidb-database/colonyManage/components/colonyBaseInfo.vue';
import Monitor from '@/package/tidb-database/colonyManage/components/monitor.vue';
import AlarmConf from '@/package/tidb-database/colonyManage/components/alarmConf.vue';
import ParameterConf from '@/package/tidb-database/colonyManage/components/parameterConf.vue';
import OperationRecord from '@/package/tidb-database/colonyManage/components/operationRecord.vue';
import { _getSystemList } from '@/services/tidb-database/databaseManage/index';
export default {
  name: 'tidb-colonyInfo',
  components: {
    ColonyBaseInfo,
    Monitor,
    AlarmConf,
    ParameterConf,
    OperationRecord
  },
  data() {
    return {
      defaultComponent: 'ColonyBaseInfo',
      viewCom: 'ColonyBaseInfo'
    };
  },
  mounted() {},
  methods: {
    changeCom(val) {
      this.viewCom = val;
    }
  }
};
</script>

<style scoped lang="less">
.tidb-colonyInfo {
  .top {
    h3 {
      margin: 20px 20px 20px 0;
      display: inline-block;
    }
  }
  .radio-button {
    margin-bottom: 20px;
  }
}
</style>

2021-9-28

element 折叠面板

  • element 折叠面板展开图表从左边换到右边的方法
vue
<template>
  <el-collapse accordion v-model="activeNames" @change="handleChange">
    <el-collapse-item name="1">
      <span class="collapse-title" slot="title">一致性 Consistency</span>
      <div>与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;</div>
      <div>在界面中一致:所有的元素和结构需保持一致,比如:设计样式、图标和文本、元素的位置等。</div>
    </el-collapse-item>
    <el-collapse-item name="2">
      <span class="collapse-title" slot="title">反馈 Feedback</span>
      <div>控制反馈:通过界面样式和交互动效让用户可以清晰的感知自己的操作;</div>
      <div>页面反馈:操作后,通过页面元素的变化清晰地展现当前状态。</div>
    </el-collapse-item>
    <el-collapse-item name="3">
      <span class="collapse-title" slot="title">效率 Efficiency</span>
      <div>简化流程:设计简洁直观的操作流程;</div>
      <div>清晰明确:语言表达清晰且表意明确,让用户快速理解进而作出决策;</div>
      <div>帮助用户识别:界面简单直白,让用户快速识别而非回忆,减少用户记忆负担。</div>
    </el-collapse-item>
    <el-collapse-item name="4">
      <span class="collapse-title" slot="title">可控 Controllability</span>
      <div>用户决策:根据场景可给予用户操作建议或安全提示,但不能代替用户进行决策;</div>
      <div>结果可控:用户可以自由的进行操作,包括撤销、回退和终止当前操作等。</div>
    </el-collapse-item>
  </el-collapse>
</template>

<style scoped lang="less">
.collapse-title {
  flex: 1 0 90%;
  order: 1;
}
.el-collapse-item__header {
  flex: 1 0 auto;
  order: -1;
}
</style>

2021-10-8

1、iframe 嵌入、postmessage

vue
<template>
  <div class="tidb-monitor">
    <div class="tabs">
      <el-tabs v-model="iframeSrc">
        <el-tab-pane label="集群监控" :name="overview"></el-tab-pane>
        <el-tab-pane label="PD节点监控" :name="pd"></el-tab-pane>
        <el-tab-pane label="TiDB节点监控" :name="tidb"></el-tab-pane>
        <el-tab-pane label="TiKV节点监控" :name="tikv"></el-tab-pane>
        <el-tab-pane label="TiFlash节点监控" :name="tiflash"></el-tab-pane>
        <el-tab-pane label="TiDB dashboard" :name="dashboard"></el-tab-pane>
      </el-tabs>
    </div>
    <iframe id="monitor" @load="postMsg" ref="monitor" :src="iframeSrc" frameborder="0"></iframe>
  </div>
</template>
<script>
import { _getColonySystemMonitor } from '@/services/tidb-database/colonyManage/index';
export default {
  name: 'tidb-monitor',
  data() {
    return {
      id: null, //集群 id
      overview: null, //全局监控链接
      pd: null, //PD监控链接
      tidb: null, //TiDB监控链接
      tikv: null, //TiKV监控链接
      tiflash: null, //TiFlash监控链接
      dashboard: null, //Tidb Dashboard链接
      token: null, //Tidb Dashboard访问token
      iframeSrc: null //iframe 的 src 地址
    };
  },
  created() {
    this.id = this.$route.query.id;
  },
  mounted() {
    this.init();
  },
  methods: {
    init() {
      this.getColonySystemMonitor();
    },
    // 获取所有监控 ifram src 地址
    async getColonySystemMonitor() {
      const params = {
        id: this.id
      };
      const res = await _getColonySystemMonitor(params);
      if (res.success) {
        const result = res.result;
        this.overview = result.overview;
        this.pd = result.pd;
        this.tidb = result.tidb;
        this.tikv = result.tikv;
        this.tiflash = result.tiflash;
        this.dashboard = result.dashboard;
        this.token = result.token;
        this.iframeSrc = this.overview;
      }
    },
    // 利用 postMessage 与 iframe 窗口通信进行登录操作
    postMsg() {
      this.$refs.monitor.contentWindow.postMessage(
        {
          type: 'DASHBOARD_PORTAL_EVENT',
          token: this.token,
          lang: 'zh',
          hideNav: false,
          redirectPath: '/overview'
        },
        '*'
      );
    }
  }
};
</script>

<style scoped lang="less">
.tidb-monitor {
  height: 100%;
  width: 100%;
  .tabs {
    height: 50px;
  }
  #monitor {
    width: 100%;
    border: none;
    margin: 0;
    padding: 0;
    z-index: 999999;
    height: calc(~'100% - 50px');
  }
}
</style>

2021-10-9

1、自定义 hook—useLocalStorage

2、elementui 更改 el-table 表头背景颜色和字体颜色

html
<el-table :header-cell-style="{background:'#eef1f6',color:'#606266'}"> ... </el-table>

3、element Tooltip 背景颜色样式修改

  • 使用这个属性popper-class添加一个 class.
html
<el-tooltip popper-class="atooltip" effect="light" content="常规业务推荐使用,不限系统" placement="top">
  <i class="el-icon-warning"></i>
</el-tooltip>
  • 重置样式重写一个 style 文件,就在当前组价创建也可以,但是不能 添加 scope,否则无效
css
<style lang="less">
.atooltip.el-tooltip__popper {
    border: none;
    box-shadow: 2px 2px 5px #ccc, -2px -2px 5px #ccc;
}
.atooltip.el-tooltip__popper[x-placement^='top'] .popper__arrow {
    border-top-color: #fff;
}
.atooltip.el-tooltip__popper[x-placement^='top'] .popper__arrow:after {
    border-top-color: #fff;
}
.atooltip {
    background: #fff !important;
}
</style>

2021-10-11

1、8-20 位,至少包含字母、数字、特殊符号中的任意两种正则

js
/^(?:(?=.*[a-zA-Z])(?=.*[\d])|(?=.*[!#+,.\\=:=@-])(?=.*[\d])|(?=.*[!#+,.\\=:=@-])(?=.*[a-zA-Z])).{8,20}$/;

2021-10-13

1、vue 中使用 css3 的 calc()函数问题

  • 1.常用的长度值几乎都可以使用 calc()函数进行计算(包括%,px 等),calc()函数支持 “+”, “-”, “*”, “/” 运算,运算符前后必须空格隔开,否则不生效。
  • 2.calc()函数在 less 中不能使用不生效(less 的运算方式和 calc 发送了冲突),比如 calc(100% - 250px )会被编译为 calc(-150% ),解决办法将函数写为 calc(~'100% - 250px')。
css
<style scoped lang="less">
.tidb-monitor {
    height: 100%;
    width: 100%;
    .tabs {
        height: 50px;
    }
    #monitor {
        width: 100%;
        border: none;
        margin: 0;
        padding: 0;
        z-index: 999999;
        height: calc(~'100% - 50px');
    }
}
</style>

2021-10-14

1、vue 中的 filters

  • vue 中的 filters 也是不能用 this 的
  • 所以在 filters 中想要用 data 中的数据,必须直接在 template 中传过来。
  • 在 filters 中函数接受的第一个参数默认是过滤的目标值,从第二个参数才是传进来的参数
vue
<el-table-column prop="status" label="状态">
    <template slot-scope="scope">
        {{ scope.row.status | handleStatus(statusObjArr) }}
    </template>
</el-table-column>

<script>
export default {
  filters: {
    handleStatus(val, statusObjArr) {
      let targetStatus = '';
      statusObjArr.forEach((item) => {
        if (item.value === val) {
          targetStatus = item.label;
        }
      });
      return targetStatus;
    }
  }
};
</script>

2021-10-15

1、编辑器实战

js
import * as monaco from 'monaco-editor';
import { format } from 'sql-formatter';

class MonacoEditor {
  constructor() {
    // 正常情况系编辑器实例
    this.monacoEditorInstance = null;
    // diff下编辑器实例
    this.diffMonacoEditorInstance = null;
    this.defaultOptions = {
      selectOnLineNumbers: true,
      roundedSelection: true,
      readOnly: false, // 只读
      cursorStyle: 'line', //光标样式
      automaticLayout: true, //自动布局
      glyphMargin: true, //字形边缘
      useTabStops: false,
      fontSize: 14, //字体大小
      autoIndent: true, //自动布局
      quickSuggestionsDelay: 0, //代码提示延时
      language: 'sql', //语言
      theme: 'vs',
      formatOnType: true,
      minimap: {
        enabled: false
      },
      scrollBeyondLastLine: false
    };
    // 当前编辑器是否处于diff的状态
    this.currentEditorDiff = false;
    // 变更堆栈信息
    // this.viewState = null;
  }

  /**
   * 初始化
   * @param domId
   * @param options
   */
  init(domId = '', options = {}) {
    if (!this.monacoEditorInstance) {
      const el = document.getElementById(domId);
      if (el) {
        const allOptions = Object.assign(this.defaultOptions, options);
        this.monacoEditorInstance = monaco.editor.create(el, allOptions);
        this.registerFormat(allOptions.language);
        this.addListenResize();
      } else {
        throw new Error('未找到dom');
      }
    }
  }

  // 编辑器内容变更
  onDidChangeModelContent(callback = undefined) {
    if (callback) {
      const instance = this.getEditor(this.currentEditorDiff);
      if (instance) {
        instance.onDidChangeModelContent(() => {
          callback && callback();
        });
      }
    }
  }
  /**
   * 销毁
   */
  destroyed() {
    if (this.monacoEditorInstance) {
      this.monacoEditorInstance.dispose();
      this.monacoEditorInstance = null;
    }
    if (this.diffMonacoEditorInstance) {
      this.diffMonacoEditorInstance?.dispose();
      this.diffMonacoEditorInstance = null;
    }
  }

  // 添加监听
  addListenResize() {
    window.addEventListener('resize', () => {
      if (this.monacoEditorInstance) {
        this.monacoEditorInstance.layout();
      }
    });
  }

  /**
   * 设置编辑器的值
   * @param value
   */
  setValues(value) {
    const instance = this.getEditor(this.currentEditorDiff);
    if (instance) {
      if (!value) {
        value = '';
      }
      instance.setValue(value);
    }
  }
  // 获取编辑的值
  getValues() {
    const instance = this.getEditor(this.currentEditorDiff);
    if (instance) {
      return instance.getValue();
    }
  }
  // 获取编辑器中选中的值
  getSelectedValue() {
    const instance = this.getEditor(this.currentEditorDiff);
    if (instance) {
      return instance.getModel().getValueInRange(instance.getSelection());
    }
  }
  // 撤销
  revoke() {
    const instance = this.getEditor(this.currentEditorDiff);
    if (instance) {
      instance.trigger('anyString', 'undo');
    }
  }

  // 重做
  redo() {
    const instance = this.getEditor(this.currentEditorDiff);
    if (instance) {
      instance.trigger('anyString', 'redo');
    }
  }

  // 格式化
  format() {
    const instance = this.getEditor(this.currentEditorDiff);
    if (instance) {
      instance.trigger('anyString', 'editor.action.formatDocument');
    }
  }

  // 注册格式化方法
  registerFormat(type) {
    switch (type) {
      case 'sql': {
        monaco.languages.registerDocumentFormattingEditProvider('sql', {
          async provideDocumentFormattingEdits(model) {
            let formatted = await format(model.getValue());
            return [
              {
                range: model.getFullModelRange(),
                text: formatted
              }
            ];
          }
        });
      }
    }
  }

  /**
   * 版本对比
   * @param domId
   * @param lhsData
   * @param rhsData
   */
  diff(domId, lhsData, rhsData, languages = 'sql', changeCallback = undefined) {
    if (!this.diffMonacoEditorInstance) {
      this.diffMonacoEditorInstance = monaco.editor.createDiffEditor(document.getElementById(domId), {
        enableSplitViewResizing: false,
        originalEditable: true,
        scrollBeyondLastLine: false
      });
    }
    let lhsModel = monaco.editor.createModel(lhsData, languages);
    let rhsModel = monaco.editor.createModel(rhsData, languages);

    this.diffMonacoEditorInstance.setModel({
      original: lhsModel,
      modified: rhsModel
    });
    // 设置右侧diff只读
    this.diffMonacoEditorInstance.getModifiedEditor().updateOptions({ readOnly: true });
    /**
     * hack写法
     * @type {Element}
     */
    const closeDiffReviewEl = document.getElementsByClassName('close-diff-review')[0];
    if (closeDiffReviewEl) {
      closeDiffReviewEl.click();
    }
  }

  /**
   * 设置当前编辑器的状态
   * @returns {boolean}
   */
  setCurrentEditorStatus(status) {
    this.currentEditorDiff = status;
  }

  /**
   * 获取当前编辑器的状态
   * @returns {boolean}
   */
  getCurrentEditorStatus() {
    return this.currentEditorDiff;
  }

  /**
   * 获取当前实例
   * @returns {null}
   */
  getEditor(isDiff = false) {
    if (isDiff) {
      return this.diffMonacoEditorInstance?.getOriginalEditor();
    } else {
      return this.monacoEditorInstance;
    }
  }

  /**
   * 设置编辑器为只读
   */
  setEditorReadOnly() {
    const instance = this.getEditor(this.currentEditorDiff);
    instance.updateOptions({ readOnly: true });
  }

  // /**
  //  * 保存变更的堆栈信息
  //  */
  // saveViewState() {
  //     const instance = this.getEditor(this.currentEditorDiff);
  //     if (instance) {
  //         this.viewState = instance.getModel();
  //         debugger;
  //     }
  // }
  //
  // /**
  //  * 恢复变更记录
  //  */
  // restoreViewState() {
  //     const instance = this.getEditor(this.currentEditorDiff);
  //     if (instance) {
  //         instance.restoreViewState(this.viewState);
  //     }
  // }
}

export default new MonacoEditor();

2021-10-20

1、Element 控制日期选择框只能选择一段时间范围内

  • 只能选择 14 天内的值
js
pickerOptions: {
  onPick: ({ maxDate, minDate }) => {
      this.min = minDate && minDate.getTime();
  },
  disabledDate: (time) => {
      let m = 13 * 24 * 60 * 60 * 1000;
      if (this.min) {
          return time.getTime() > this.min + m || time.getTime() < this.min - m;
      }else {
          return false;
      }
  },
},
  • 想要清除 disabledDate,方法如下:
  • 1、给日期选择框绑定一个@on-change="handleChange"
js
handleChange(val) {
    if (!val) {
        this.min = null;
    }
},
  • 2、这样点击日期选择框清除按钮就会触发事件取消disabledDate

2、Element 控制日期选择框设定上上个星期六星期天

js
const start = new Date();
const end = new Date();
const day = new Date().getDay(); //当前是星期几
start.setTime(start.getTime() - 3600 * 1000 * 24 * (day + 1));
end.setTime(end.getTime() - 3600 * 1000 * 24 * day);

3、日期格式化工具化函数

js
dateFormat(tempDate, fmt) {
        var o = {
            'M+': tempDate.getMonth() + 1, //月份
            'd+': tempDate.getDate(), //日
            'H+': tempDate.getHours(), //小时
            'm+': tempDate.getMinutes(), //分
            's+': tempDate.getSeconds(), //秒
            'q+': Math.floor((tempDate.getMonth() + 3) / 3), //季度
            S: tempDate.getMilliseconds() //毫秒
        };
        if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (tempDate.getFullYear() + '').substr(4 - RegExp.$1.length));
        for (var k in o)
            if (new RegExp('(' + k + ')').test(fmt))
                fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length));
        return fmt;
    }

//使用方法
dateFormat(Date(), 'yyyy-MM-dd')

2021-10-21

1、误把 .DS_Store 提交到了远程分支处理方法

  • 1、终端中输入下面命令删除所有.DS_Store 文件。

    shell
    find . -name .DS_Store -print0 | xargs -0 git rm -f --ignore-unmatch
  • 2、然后提交并推送更改以从远程仓库中删除.DS_Store

shell
git commit -m "Remove .DS_Store from everywhere"
git push origin master

2021-10-29

1、Number.isInteger()—方法用来判断给定的参数是否为整数。(有点问题)

  • 问题
js
Number.isInteger(8.0); //true
  • 原因
js
Number.isInteger =
  Number.isInteger ||
  function (value) {
    return typeof value === 'number' && isFinite(value) && Math.floor(value) === value; //原因
  };

2021-11-03

1、element 在多选表格中加一行不需要 CheckBox 的问题

  • 效果如下图
image-20211104170145638
  • 主要代码如下
js
<ui-table-pro
   ....其他属性
   :cell-class-name="cellClassNameFn"
>
  <ui-table-column
      type="selection"
      width="60"
      :selectable="handledisabled"
  ></ui-table-column>
</ui-table-pro>

// 给特定的单元格设置类名
cellClassNameFn(row) {
    if (row.rowIndex === 0) {
        // 第一列且状态为 1 通过自定义样式隐藏复选框
        return 'table-column-hidden';
    }
},
//将这一列的 CheckBox disabled,防止全选时被选中
handledisabled(row, index) {
    if (index === 0) {
        return false;
    } else {
        return true;
    }
},

//隐藏 CheckBox 的样式
/deep/ .table-column-hidden .checkbox {
    display: none !important;
}

2021-11-08

1、常见常用的伪元素

选择器示例示例描述
::afterp::after在每个 p 元素之后插入内容。
::beforep::before在每个 p 元素之前插入内容。
::first-letterp::first-letter选择每个 p 元素的首字母。
::first-lineP::first-line选择每个 p 元素的首行。
::selectionp::selection选择用户选择的元素部分。

2021-11-09

1、css 实现模糊效果

  • backdrop-filter
  • filter

2、flex 布局子元素高度设置无效的原因及解决办法

原因

  • 定义为 flex 布局元素的子元素,自动获得了 flex-shrink 的属性,这个属性是什么意思呢?
  • 就是告诉子元素当父元素宽度不够用时,自己调整自己所占的宽度比
  • 这个 flex-shrink 设置为 1 时,表示所有子元素大家同时缩小来适应总宽度。
  • 当 flex-shrink 设置为 0 时,表示大家都不缩小适应。
  • 原文链接:https://blog.csdn.net/ycg5250/article/details/117303044

解决办法

  • 给子元素设置 flex-shrink:0

2021-11-11

1、css 选择器(:nth-child)

css
span: nth-child(-n + 3) //匹配前三个子元素中的span元素。;

2021-11-18

1、element 级联选择器获取 label 的值

js
this.$refs.myCascader.getCheckedNodes()[0].pathLabels;

2021-11-22

js
/*
 * @Author: wangzhihao
 * @Date: 2021-11-22 12:51:36
 * @LastEditors: wangzhihao
 * @LastEditTime: 2021-11-22 13:16:46
 */
//有意思的操作 可以改变 instanceof 操作符的结果
class Bar {}
class Baz extends Bar {
  static [Symbol.hasInstance]() {
    return false;
  }
}
let b = new Baz();
console.log(Bar[Symbol.hasInstance](b)); // true
console.log(b instanceof Bar); // true
console.log(Baz[Symbol.hasInstance](b)); // false
console.log(b instanceof Baz); // false

//柯里化函数
const sum = function () {
  const prev = [...arguments];
  const fn = function () {
    const cur = [...arguments];
    return sum.apply(null, prev.concat(cur));
  };
  /**
   * 优先走 valueOf
   */
  // fn.valueOf = () => {
  //     return prev.reduce((p,c) => p - c);
  // }
  fn.toString = () => {
    return prev.reduce((p, c) => p + c);
  };
  return fn;
};

// console.log(+sum(1,3)(1,4)(5)(2,0)); // 放开 valueOf 的结果就是 -14
console.log(+sum(1, 3)(1, 4)(5)(2, 0)); //16

2021-11-23

1、浏览器提供的复制操作

  • navigator.clipboard.writeText(value)
  • 更多操作详见阮一峰
js
async copyMiyaoAk() {
    try {
        await navigator.clipboard.writeText(this.AK);
        this.$toast({
            content: '复制成功!',
            type: 'success',
        });
    } catch (err) {
        this.$toast({
            content: '复制失败!',
            type: 'error',
        });
    }
},

2021-11-25

1、webpack 端口

  • 用在 webpack 配置里,参数可选。如下配置会优先以 8077 端口启动服务器,如果 8077 被占用了,会自动寻找下一个可用端口。
js
const portFinderSync = require('portfinder-sync')
devServer: {
    port: portFinderSync.getPort(8077),
}

2、css 中定义 class 时,中间有空格和没空格的区别是什么?

  • example:两个 css 如下:
css
.example .pp {
  color: orange;
}
.example.pp2 {
  color: green;
}
  • 第一个样式生效的 html
html
<p class="example">
  文字文字
  <span class="pp">pp这个class生效</span>
</p>
  • 第二个样式生效的 html
html
<p class="example pp2">文字文字 pp2这个class生效</p>

2021-12-06

1、正则表达式-仅支持小写字母、数字、下划线,且不能以下划线开头/结尾

js
/^(?!_)(?!.*_$)[a-zA-Z0-9_]+$/;

2、正则断言相关

2021-12-14

1、video 标签相关

  • 视频自动循环播放

video标签上添加autoplayloop 属性,需手动点击播放才可自动循环播放,想要一打开就让 video 自动循环播放需要添加muted属性,具体代码实现如下:

html
<video src="@/assets/homepage/homepage.mp4" :autoplay="true" :loop="true" muted="muted"></video>

MIT Licensed