胖蔡叨叨叨
你听我说

jQuery从$开始

胖蔡阅读(72)

jQuery requires a window with a document.jQuery版本3.6.0

使用jQuery的第一行代码总是从$(document).ready(function(){});或者$(function(){});开始,我从jQuery执行过程去了解$是怎么被识别并被使用的。

jQuery 入口函数

jQuery根据不同的js运行时环境将jQuery实例挂载到不同属性上。如果是浏览器引用,则挂载到window的$上,如果是require方式在例如node.js环境引用,则需要判断当前环境的文档流在哪一层(写此博客时还没有搭建node环境,这里没有做验证,只是想法猜测),jQuery需要运行在有文档流的环境中。

javascript
(function(global, factory){
	if ( typeof module === "object" && typeof module.exports === "object" ) {
		module.exports = global.document ?
			factory( global, true ) :
			function( w ) {
				if ( !w.document ) {
					throw new Error( "jQuery requires a window with a document" );
				}
				return factory( w );
			};
	} else {
		factory( global );
	}
	
})(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {

	var jQuery = function( selector, context ) {
		return ...;
	};
	
	var _jQuery = window.jQuery, _$ = window.$;

    jQuery.noConflict = function( deep ) {
    	if ( window.$ === jQuery ) {
	    	window.$ = _$;
	    }

    	if ( deep && window.jQuery === jQuery ) {
	    	window.jQuery = _jQuery;
    	}

	return jQuery;
    };

	if ( typeof noGlobal === "undefined" ) {
		window.jQuery = window.$ = jQuery;
	};
	
	return jQuery;
});

jQuery提供了noConflict方法,解决$可能与其他框架同名产生冲突的问题,同时如果jQuery运行在没有window属性的环境中,也不能使用$。可以通过noConflict方法重新定义一个属性。

jQuery初始化

预告为了防止文档在没有完全加载之前运行jQuery代码,即在Dom加载完成后才可以对Dom进行操作,jQuery所有函数都位于document ready函数中。

Js 过滤富文本html标签提取纯文本内容

胖蔡阅读(144)

需要将富文本中的标签和特殊字符过滤提取纯文本内容,就上网找了下,但是发现都不是我想要的。所以找了下之前自己写的过滤方法,在这里记录下,方便之后使用查找。功能比较简单,就直接上代码。

 

f445b9f60a137c1-1

 

/**
 * 将富文本转换为纯文本内容
 * @function
 * @param {*} html  富文本内容
 * @returns {string} 返回纯文本内容、
 */

export function getSimpleText(html) {
  const re1 = new RegExp("<.+?>", "g"); // 匹配html标签的正则表达式,"g"是搜索匹配多个符合的内容
  const arrEntities = { 'lt': '<', 'gt': '>', 'nbsp': ' ', 'amp': '&', 'quot': '"', 'ldquo': '“', 'mdash': '—', "rdquo": '”' };
  return html.replace(re1, '').replace(/&(lt|gt|nbsp|amp|quot|ldquo|mdash|rdquo);/ig, function (all, t) {
    return arrEntities[t];
  });
}

 

 

过滤替换单个标签可以通过如下的方法实现,我这里是用于替换 img 标签,大家可以根据需要将img标签改为需要替换查找的标签。

 

/**
 * 替换富文本中的image标签,使用占位符显示,hover弹框显示
 * @param {*} html
 */

export function replaceHtmlImages(html) {
  return html.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, function (match, capture) {
    return `<img class="hoverImg" onClick="openImg('${capture}')"  style="cursor:pointer;height:18px !important;width:18px !important;"  src="${picIcon}"  alt="图片" />`;
  });
}

 

大家自取自用,多多益善~

使用slatejs实现自定义元素

胖蔡阅读(103)

 

背景

项目需要,需要使用可实现自定义的富文本编辑器。所以就找了市面上的一些富文本编辑器进行对比选型。由于我们采用的是react实现的前端开发,本次需要对于富文本编辑器需要有个较为深度的自定义。所以,我对市面上常用的几款富文本编辑器做了一个选型对比表格。

 

框架
功能
实操
基础文本编辑
表格操作
表格语义化
上手难度
风险项
优势
是否付费
Draft.js
支持
支持
支持,自定义组件【契合度高】
中度
1、操作栏需要自己绘制,适配,基础功能非开箱即用
2、不支持osx按键绑定
3、UI绘制、适配可能比较耗时
4、不兼容html互转
5、复杂结构【table】编辑器会卡顿
1、facebook自研,支持react,组件化较高
2、插件支持
3、数据视图分离
Slate.js
支持
支持
支持
中度
1、对外复制粘贴功能不完善
2、slate-react实现基于hook,依赖hook
1、核心包与页面渲染包分离,支持前端框架的自定义
2、插件支持
3、支持react
Quill.js
支持
支持,需要三方插件
不支持
一般
1、富文本复制存在丢失问题
2、功能定制较为有限
3、不支持插件
1、有预设样式
TinyMCE
支持
支持
支持
中度
1、不支持react组件化,需要兼容实现,与vue更友好
2、2020年开始基于slate进行富文本的开发
3、高级功能需要收费,其中包括表格
1、比较成熟
2、预设样式较为美观
3、插件支持
有免费版和商用版
KendoReact
支持
支持
支持
/
需要付费,首次使用可以申请一个免费期限
1、高性能
2、高度可定制
3、插件支持
4、react支持

 

综合各方面元素,最终决定了选择slatejs作为富文本开发框架来帮助实现功能。

 

使用

既然选定了slatejs来作为富文本编辑器。slatejs怎么怎么优美,又是怎么怎么强大,咱先暂时抛开,看看如何去使用才是王道。

安装

常规第一步,不论啥框架,先把依赖安装了再说

$ npm i slate slate-react --save-dev // 或者
$ yarn add slate slate-react -D

基于react去应用了,react和react-dom安装就不说了,忘记装的,自己装下。

 

 

使用

这里,我就不分步骤一个个放代码了,给个终结的,再简单介绍几句就完事了。你好我好大家都好。

import React, { useState, useCallback } from 'react'
import { createEditor, Editor, Transforms } from 'slate';
import { Slate, Editable, withReact } from 'slate-react';

const initialValue = [   // 设置操作栏初始配置
  {
    type: 'paragraph',
    children: [{
      text: 'A line of text in a paragraph.'
    }],

  },

]

const CodeElement = (props) => {   // 自定义格式化代码组件
  return (
    <pre {...props.attributes}>
      <code>{props.children}</code>
    </pre>);
};

const DefaultElement = (props) => { return <p {...props.attributes}>{props.children}</p>; };


const SlateEditor: React.FC = (props) => {
  const editor = useMemo(() => withReact(createEditor()), []);
  const renderElement  useCallback((props) => {
    switch (props.element.type) {
      case 'code':
        return <CodeElement {...props} />;
      default:
        return <DefaultElement {...props} />;
    }
  }, []);

  return (
    <Slate editor={editor} value={initialValue}>
      <Editable
        onKeyDown={(event) => {
          if (event.key === '`' && event.ctrlKey) {
            event.preventDefault();
            Transforms.setNodes(
              editor,
              { type: 'code' },
              { match: (n) => Editor.isBlock(editor, n) },
            );
          }
        }
        }
      />
    </Slate>
  );
};
如上通过Slate 配置 onKeyDown来创建富文本的句柄监听按键实践,通过Tranforms添加自定义组件CodeElement。

你需要知道的JavaScript数据类型

胖蔡阅读(129)

JavaScript 是一种解释型语言,JavaScript指令以纯文本形式传递给浏览器,然后依次执行。因为不需要执行编译成计算机的二进制文件执行,使得JavaScript语言更加便于阅读、编写、测试。本篇文章主要是对JavaScript中的数据类型进行一个整理分享。

 

JavaScript属于”宽松类型”的编程语言,这也意味着JavaScript中的变量在不同的场合可以被解释为不同的类型。在JavaScript的世界里,不必如Java等其他语言那样需要事先申明变量类型,JavaScript的解释器会自己根据场景做出自己的判断。一般意义上,我们将JavaScript的类型分为如下这六类:

  • Number类型
  • Boolean 类型
  • String类型
  • Null
  • Undefined
  • Object

为了方便确认变量的数据类型,JS中提供了typeof操作符来检测当前变量的数据类型:

typeof 300
< 'number'

 

String 类型

string类型是一串字符组成,字符串被引号包裹,可以选择使用单引号或双引号包裹:

var testString = 'i'm a string~'
var testString = "haha~"

字符串还有些特殊的文本通过转义符\来转换显示,常见的转义符号如下:

符号 说明
\0 空字符
\' 单引号
\" 双引号
\\ 反斜杠
\n 换行
\r 回车
\v 垂直制表符
\t 水平制表符
\b 退格
\f 换页
\uXXXX unicode 码
\u{X} … \u{XXXXXX} unicode codepoint
\xXX Latin-1 字符(x小写)

string也提供了很多很便捷的操作、方法等

  • length:获取当前字符串长度属性
  • replace:字符串替换方法方法
  • search:检索字符串指定子串方法
  • lastIndexOf、indexOf:字符串索引查找方法
  • slice、substring 、substr:字符串截取方法
  • split:字符串拆分方法

 

Number 类型

数字类型是比较宽泛的,其实在JS中我们可以把Number类型分为如下几种类型:

  • 整型
  • 浮点型
  • 进制【二进制、八进制、十进制、十六进制】
var a1 = 10   // 整型
var a2 =  1.1 // 浮点型
var a3 = 1001  // 十进制
var a4 = 0o77 // 八进制
var a5 = 0xAE // 十六进制
var a6 = 0b1101  //二进制

常用方法

  • toString:将数字作为字符串返回
  • toFixed:返回一个指定小数位的字符串
  • toExponential:数字进行舍入并且用指数表示法来表示,传递给改方法的参数制定了转换之后小数点后面的字符数字

 

Boolean 类型

bool类型的数据只有两个值:true(真)和false(假)。一般用于程序中控制逻辑判断。

var x = true;
var y = false;

需要注意的是,JS中把非0值作为true来处理,把0作为false来处理,除了0之外,如下这些值也会被JS当做false处理:

  • false
  • undefined(非字符串)
  • null
  • NaN
  • 0
  • “”(空字符串)

Null和Undefined 类型

JavaScript中有两个比较直接的类型:Null和undefined。它们就如它们字面展示的意思一样:空和未定义。通常情况下,我们会将null和undefined称为假值,意思是“并非完全是假的,但它们可以解释为假”。

 

Object类型

js中对象是一组属性与方法的集合。所以一般情况下,我们可以将Array、Object(相对于Array)、Function都可以理解为Object类型。

var  user = {
     age:29,
     sex:'male',
     name:'胖蔡'
 };
var colors = ['red','green','yellow','blue'];

var fun = function getColor(colors,index) {
    return colors[index];
};


 

使用docxjs实现前端的word生成

胖蔡阅读(229)

Docx 可以帮助我们实现ts/js代码生成*.docx文件的功能,它可以同时被运用于Node端和浏览器端。使用docx基本可以实现大多数Microsoft Word文档英文版的绝大数API操作,但由于Microsoft Word文档中英文版本的差异,对于中文版本的功能、属性控制还是得需要通过自定义xml的方式来实现。本篇文章简单介绍下如何使用docxjs实现word文档的生成。

2c26b65871ed768

可访问官网查看官方文档:https://docx.js.org/   。我始终坚持认为,官方文档是最能帮助开发者的。

安装

$ npm install -s docx

基本用法

import * as fs from "fs"; 
import { Document, Packer, Paragraph, TextRun } from "docx";

 // 文档类Document中包含章节数组sections,可向其中添加多个章节 
const doc = new Document({ 
      sections: [{ 
          properties: {}, 
          children: [ new Paragraph({ 
             children: [ new TextRun("Hello World"), 
                  new TextRun({ text: "Foo Bar", bold: true, }),
                  new TextRun({ text: "\tGithub is the best", bold: true, }), ], }), ], }], }); 

// 通过fs实现将docx从buffer流转化成*.docx文件

 Packer.toBuffer(doc).then((buffer) => { fs.writeFileSync("My Document.docx", buffer); }); // 完成下载


通过上方的示例,可以发现docx将整个文档拆分成多个区域块,然后通过组合、包含区域块的方式实现文档的输出。下面,简单介绍下docx中常用的几个模块类。

Document

Document可以说是docx梦开始的地方。其实,我们也可以将其理解为docx文档本生。一个docx文档有且仅有一个document生成。可以通过如下方式生成一个基本的document对象。
const doc = new docx.Document();
document很重要,包含的属性有如下:
  • creator
  • description
  • title
  • subject
  • keywords
  • lastModifiedBy
  • revision
  • externalStyles
  • styles
  • numbering
  • footnotes
  • hyperlinks
  • background
  • sections
这里重点介绍externalStyles、styles。

externalStyles

这个属性对于我们的中文用户来说尤为重要,这或许也是docx作者考虑可能属性覆盖不全,所以特意留下的一个自定义属性样式的窗口。功能可以说是及其省事。我们完全可以通过解压docx文档,将其中的xml属性配置文件拿出来做些微的修改来实现我们的功能。大致就像下面这样。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:styles xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
    xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
    xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"
    xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" mc:Ignorable="w14 w15">
    <w:docDefaults>
        <w:rPrDefault>
            <w:rPr>
                <w:rFonts w:asciiTheme="minorHAnsi" w:eastAsiaTheme="minorHAnsi" w:hAnsiTheme="minorHAnsi" w:cstheme="minorBidi"/>
                <w:sz w:val="22"/>
                <w:szCs w:val="22"/>
                <w:lang w:val="en-GB" w:eastAsia="en-US" w:bidi="ar-SA"/>
            </w:rPr>
        </w:rPrDefault>
        <w:pPrDefault>
            <w:pPr>
                <w:spacing w:after="160" w:line="259" w:lineRule="auto"/>
            </w:pPr>
        </w:pPrDefault>
    </w:docDefaults>

 

    <w:style w:type="paragraph" w:default="1" w:styleId="Normal">
        <w:name w:val="Normal"/>
        <w:qFormat/>
        <w:rsid w:val="000D2D99"/>
        <w:rPr>
            <w:rFonts w:ascii="MV Boli" w:hAnsi="MV Boli"/>
            <w:color w:val="C45911" w:themeColor="accent2" w:themeShade="BF"/>
        </w:rPr>
    </w:style>

    <w:style w:type="paragraph" w:styleId="Heading1">
        <w:name w:val="heading 1"/>
        <w:basedOn w:val="Normal"/>
        <w:next w:val="Normal"/>
        <w:link w:val="Heading1Char"/>
        <w:uiPriority w:val="9"/>
        <w:qFormat/>
        <w:rsid w:val="000D2D99"/>
        <w:pPr>
            <w:keepNext/>
            <w:keepLines/>
            <w:spacing w:before="240" w:after="0"/>
            <w:outlineLvl w:val="0"/>
        </w:pPr>
        <w:rPr>
            <w:rFonts w:ascii="Impact" w:eastAsiaTheme="majorEastAsia" w:hAnsi="Impact" w:cstheme="majorBidi"/>
            <w:color w:val="538135" w:themeColor="accent6" w:themeShade="BF"/>
            <w:sz w:val="32"/>
            <w:szCs w:val="32"/>
        </w:rPr>
    </w:style>

    <w:style w:type="character" w:default="1" w:styleId="DefaultParagraphFont">
        <w:name w:val="Default Paragraph Font"/>
        <w:uiPriority w:val="1"/>
        <w:semiHidden/>
        <w:unhideWhenUsed/>
    </w:style>

    <w:style w:type="table" w:default="1" w:styleId="TableNormal">
        <w:name w:val="Normal Table"/>
        <w:uiPriority w:val="99"/>
        <w:semiHidden/>
        <w:unhideWhenUsed/>
        <w:tblPr>
            <w:tblInd w:w="0" w:type="dxa"/>
            <w:tblCellMar>
                <w:top w:w="0" w:type="dxa"/>
                <w:left w:w="108" w:type="dxa"/>
                <w:bottom w:w="0" w:type="dxa"/>
                <w:right w:w="108" w:type="dxa"/>
            </w:tblCellMar>
        </w:tblPr>
    </w:style>

    <w:style w:type="table" w:customStyle="1" w:styleId="MyCustomTableStyle">
        <w:name w:val="My Custom TableStyle"/>
        <w:uiPriority w:val="99"/>
        <w:rsid w:val="00C36BA2"/>
        <w:pPr>
            <w:spacing w:after="0" w:line="240" w:lineRule="auto"/>
        </w:pPr>
        <w:rPr>
            <w:rFonts w:ascii="Comic Sans MS" w:hAnsi="Comic Sans MS"/>
            <w:szCs w:val="20"/>
            <w:lang w:val="en-US"/>
        </w:rPr>

        <w:tcPr>
            <w:shd w:val="clear" w:color="auto" w:fill="auto"/>
        </w:tcPr>

        <w:tblStylePr w:type="firstRow">

            <w:rPr>
                <w:b/>
                <w:bCs/>
                <w:color w:val="FFFFFF" w:themeColor="background1"/>
            </w:rPr>

            <w:tblPr/>

            <w:tcPr>
                <w:tcBorders>
                    <w:tl2br w:val="none" w:sz="0" w:space="0" w:color="auto"/>
                    <w:tr2bl w:val="none" w:sz="0" w:space="0" w:color="auto"/>
                </w:tcBorders>
                <w:shd w:val="clear" w:color="auto" w:fill="4472C4" w:themeFill="accent1"/>
            </w:tcPr>

        </w:tblStylePr>

    </w:style>

    <w:style w:type="numbering" w:default="1" w:styleId="NoList">
        <w:name w:val="No List"/>
        <w:uiPriority w:val="99"/>
        <w:semiHidden/>
        <w:unhideWhenUsed/>
    </w:style>

    <w:style w:type="paragraph" w:customStyle="1" w:styleId="CustomStyle">
        <w:name w:val="Custom Style"/>
        <w:basedOn w:val="Normal"/>
        <w:link w:val="CustomStyleChar"/>
        <w:rsid w:val="00B557A5"/>
        <w:rPr>
            <w:sz w:val="72"/>
        </w:rPr>
    </w:style>

    <w:style w:type="character" w:customStyle="1" w:styleId="CustomStyleChar">
        <w:name w:val="Custom Style Char"/>
        <w:basedOn w:val="DefaultParagraphFont"/>
        <w:link w:val="CustomStyle"/>
        <w:rsid w:val="00B557A5"/>
        <w:rPr>
            <w:rFonts w:ascii="MV Boli" w:hAnsi="MV Boli"/>
            <w:color w:val="C45911" w:themeColor="accent2" w:themeShade="BF"/>
            <w:sz w:val="72"/>
        </w:rPr>
    </w:style>

    <w:style w:type="character" w:customStyle="1" w:styleId="Heading1Char">
        <w:name w:val="Heading 1 Char"/>
        <w:basedOn w:val="DefaultParagraphFont"/>
        <w:link w:val="Heading1"/>
        <w:uiPriority w:val="9"/>
        <w:rsid w:val="000D2D99"/>
        <w:rPr>
            <w:rFonts w:ascii="Impact" w:eastAsiaTheme="majorEastAsia" w:hAnsi="Impact" w:cstheme="majorBidi"/>
            <w:color w:val="538135" w:themeColor="accent6" w:themeShade="BF"/>
            <w:sz w:val="32"/>
            <w:szCs w:val="32"/>
        </w:rPr>
    </w:style>

    <w:style w:type="paragraph" w:customStyle="1" w:styleId="MyFancyStyle">
        <w:name w:val="MyFancyStyle"/>
        <w:basedOn w:val="Normal"/>
        <w:link w:val="MyFancyStyleChar"/>
        <w:qFormat/>
        <w:rsid w:val="008802A5"/>
        <w:rPr>
            <w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman"/>
            <w:color w:val="FF0000"/>
            <w:sz w:val="144"/>
        </w:rPr>

    </w:style>

    <w:style w:type="paragraph" w:customStyle="1" w:styleId="subject">
        <w:name w:val="subject"/>
        <w:basedOn w:val="Normal"/>
        <w:link w:val="subjectChar"/>
        <w:qFormat/>
        <w:rsid w:val="008802A5"/>
        <w:textAlignment w:val="center"/>
        <w:rPr>
            <w:rFonts w:hint="eastAsia" w:ascii="仿宋_GB2312" w:hAnsi="仿宋_GB2312" w:eastAsia="仿宋_GB2312" w:cs="仿宋_GB2312"/>
            <w:sz w:val="24"/>
            <w:szCs w:val="24"/>
            <w:lang w:val="en-US" w:eastAsia="zh-CN"/>
             <w:color w:val="FF0000"/>
        </w:rPr>

    </w:style>

     <w:style w:type="character" w:customStyle="1" w:styleId="subjectChar">
        <w:name w:val="subject Char"/>
        <w:basedOn w:val="DefaultParagraphFont"/>
        <w:link w:val="subject"/>
        <w:rsid w:val="008802A5"/>

        <w:rPr>
           <w:rFonts w:hint="eastAsia" w:ascii="仿宋_GB2312" w:hAnsi="仿宋_GB2312" w:eastAsia="仿宋_GB2312" w:cs="仿宋_GB2312"/>
            <w:sz w:val="24"/>
            <w:szCs w:val="24"/>
            <w:lang w:val="en-US" w:eastAsia="zh-CN"/>
             <w:color w:val="FF0000"/>

        </w:rPr>

    </w:style>

    <w:style w:type="character" w:customStyle="1" w:styleId="MyFancyStyleChar">
        <w:name w:val="MyFancyStyle Char"/>
        <w:basedOn w:val="DefaultParagraphFont"/>
        <w:link w:val="MyFancyStyle"/>
        <w:rsid w:val="008802A5"/>

        <w:rPr>
            <w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman"/>
            <w:color w:val="FF0000"/>
            <w:sz w:val="144"/>
        </w:rPr>
    </w:style>

</w:styles>
import * as fs from "fs"; 
import { Document, Packer, Paragraph, TextRun } from "docx";
import  { xmlStyles } from './xmlStyles';

 // 文档类Document中包含章节数组sections,可向其中添加多个章节 
const doc = new Document({ 
      externalStyles:xmlStyles,
      sections: [{ 
          properties: {}, 
          children: [ new Paragraph({ 
             children: [ new TextRun("Hello World"), 
                  new TextRun({ text: "Foo Bar", bold: true, }),
                  new TextRun({ text: "\tGithub is the best", bold: true, }), ], }), ], }], }); 

// 通过fs实现将docx从buffer流转化成*.docx文件

 Packer.toBuffer(doc).then((buffer) => { fs.writeFileSync("My Document.docx", buffer); }); // 完成下载


styles

styles也属于样式自定义的一种方式,但这里的方式定义方式是局限于当前docx提供的属性,我们可以在docx的section、Paragraph、Text、Header等中使用。
import * as fs from "fs"; 
import { Document, Packer, Paragraph, TextRun } from "docx";


/**  * 获取自定义段落格式  */ 

function getParagraphStyles() {   
  return [ {        
     id: 'tips', // 温馨提示      
     name: 'tips',           
     basedOn: "Normal",          
     next: "Normal",            
     quickFormat: true,            
     paragraph: {
                 border: {
                     top: borderStyle,
                     left: borderStyle,
                     right: borderStyle, 
                    bottom: borderStyle
                 }
             }
         }, 
 {
             id: 'section', // 题型段落
             name: 'section',
             basedOn: "Normal",
             next: "Normal", 
            quickFormat: true,
             paragraph: { 
                indent: indent,
                spacing: {
                    before: '2pc'
                 }, 
            }
         },
     ]
 }

 // 文档类Document中包含章节数组sections,可向其中添加多个章节 
const doc = new Document({ 
      sections: [{ 
          properties: {}, 
          children: [ new Paragraph({ 
             children: [ new TextRun("Hello World"), 
                  new TextRun({ text: "Foo Bar", bold: true, }),
                  new TextRun({ text: "\tGithub is the best", bold: true, }), ], }), ], }], }); 

// 通过fs实现将docx从buffer流转化成*.docx文件

 Packer.toBuffer(doc).then((buffer) => { fs.writeFileSync("My Document.docx", buffer); }); // 完成下载


React使用Ant Design Pro框架导致首屏加载缓慢问题

胖蔡阅读(150)

存在问题

React项目中使用Ant Design pro框架,发现编译后前端应用的首屏加载过于缓慢,针对该问题,对改应用的生成包进行分析并优化,使其加载速度提升,用户体验更好。整个优化的思路过程如下。

1、网页登录后,首次刷新【存在路由】,加载时长过大,用户体验不佳

0
0

存在的相关关键指标:

  • DOMCOntentLoaded:7.32s
  • Load:32.21s
  • Finish:32.90s
  • layout.async.js:23.29s
  • misc.async.js:14.53s

解决思路

根据上面出现的问题分析可以通过如下几个方面对包的大小和加载的必要性进行拆分优化:
  1. 拆出页面包
  2. 裁剪vendors,将@ant-design、antd、docx包单独拆出一个文件,降低load的响应时间,
  3. 替换@ant-design/chats 为 @antv/g2plot,减少可视化组件包的大小
const webpackPlugin = config => {   // optimize chunks   config.optimization // share the same chunks across different modules     .runtimeChunk(false)     .splitChunks({       // chunks: 'async',       name: 'vendors',       chunks: 'async',       minSize: 20000,       minChunks: 1,       maxAsyncRequests: 30,       maxInitialRequests: 30,       cacheGroups: {         vendors: {           test: module => {             const packageName = getModulePackageName(module) || '';             // console.log('get splitChunks vendors:',packageName)             if (packageName) {               return [                 '@antv',                 '@ant-design',                 'antd',                 'docx',                 'l7',                 'gg-editor-core',                 'bizcharts-plugin-slider',               ].includes(packageName);             }             return false;           },           name(module) {             const packageName = getModulePackageName(module);             if (packageName) {               // console.log('get splitChunks name:', packageName);               if (['@ant-design', 'antd'].indexOf(packageName) >= 0) {                 return 'ant-design'; // visualization package               } else if (['docx'].indexOf(packageName) >= 0) {                 // 单独拆开                 return 'docx';               }             }             return 'misc';           },         },       },     }); };

结果

1、gzip降低

22165adbf146e2b
原始的
51433a6450391ca
总包大小以及layout.async.js、vendors.async.js大小均有显著缩小

2、请求数据

cd2907c13172429

存在的相关关键指标:

  • DOMCOntentLoaded:7.72s
  • Load:13.96s
  • Finish:14.68s
  • misc.async.js:3.57s
  • umi.js:7.30s

Webpack 项目添加 eslint 实现代码检测功能

胖蔡阅读(91)

项目环境及相关资料

依赖库

1、eslint-loader
想要 webpack 实现 eslint 的检测,首先需要安装 loader eslint-loader ,并在配置文件中添加加载规则:


// 使用npm
npm install --save-dev eslint-loader

// 使用yarn
$yarn add -D eslint-loader
// webpack.config.js

{
...
  module: {
    rules: [
      {
        test/\.js$/,
        loader"eslint-loader",
         exclude/node_modules/,
        enforce"pre",
        include: [path.resolve(__dirname, "src")], // 指定检查的目录
        options: {
          // 这里的配置项参数将会被传递到 eslint  CLIEngine
          formatterrequire("eslint-friendly-formatter"), // 指定错误报告的格式规范
      //  fix: true, // 自动修正,会改变文件内容,根据需要配置
        },
      },
    ];
  }
  ...
}

2、 eslint

为了能使项目具有eslint检测功能,我们需要安装eslint依赖库,并生成配置eslint配置文件.

// npm 
npm install --save-dev eslint

// yarn
yarn add -D eslint

eslint的配置文件有很多类型,可以是 .eslintrc,也可以是 .eslint.js又或者是 .eslint.json,这里以.eslintrc.js为例,给出一个示例配置:

这里需要注意的是eslint的配置,需要配置一个错误级别,通常错误级别有三个类别:

  • “off” or 0 – 关闭规则
  • “warn” or 1 – 开启规则,使用警告级别的错误:warn (不会导致程序退出)
  • “error” or 2 – 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
/* eslint-disable no-undef */
module.exports = {
    'env': {
        'browser'true,
        'es2021'true,
    },
    'extends''eslint:recommended',
    'parserOptions': {
        'ecmaVersion'13,
        'sourceType''module',
    },
    'rules': {
        'no-console': process.env.NODE_ENV === 'production''error''off'// allow console during development
        'no-debugger': process.env.NODE_ENV === 'production''error''off'// allow debugger during development
        // 以下为该项目自定义部分
        'indent': ['error'4], //缩进风格 - 开启缩进4
        'max-len': ['error', {
            'code'120,         // 强制单行的最大长度为120
            'comments'120,    // 强制单行注释的最大长度为120    
        }],
        'space-in-parens': ['error''always'], // 强制圆括号内有一个空格
        'curly': ['error''multi''consistent'], //   ifelse if  else 所有的代码块使用或者不使用大括号。
        'keyword-spacing':['warn',{         // 关键字前后需要保持至少有一个空格
            'before'true,
            'after'true,
        }],
        'lines-around-comment':['warn',{        
            'beforeBlockComment'true,
            'afterBlockComment'true,
        }],
        'one-var-declaration-per-line': ['error''initializations'], // 强制每个变量初始化语句换行
        'comma-dangle': ['error''always-multiline'], //  当最后一个元素或属性与闭括号 ]  }  不同的行时,要求使用拖尾逗号;当在 同一行时,禁止使用拖尾逗号。
        'quotes': ['error''single'], // 要求尽可能地使用单引号
        'no-nested-ternary''error'// 禁止使用嵌套的三元表达式
        'multiline-ternary': ['error''always-multiline'], // 如果表达式跨越多个行,则在三元表达式的操作数之间强制换行
        'wrap-iife': ['error''any'], // 强制总是包裹,但允许其它风格。
        'no-new-wrappers''error'// 禁止对 StringNumber  Boolean 使用 new 操作符
        'no-var''error'// 要求使用 let  const 而不是 var
        'no-duplicate-imports''error'// 禁止重复模块导入
        'eol-last': ['error''always'], //文件强制使用换行 (LF)结束最后一行
        'camelcase': ['error', {'properties''always'}], //强制驼峰法命名 
    },
};


上面配置中的 ‘extends’: ‘eslint:recommended’,为继承eslint默认的rule配置,所以一些基础的配置就不需要我们再重复配置了。上面所包含的配置属性以及相关描述,在下方给一一列出来了:

规则名 规则描述
for-direction 强制 “for” 循环中更新子句的计数器朝着正确的方向移动
getter-return 强制 getter 函数中出现 return 语句
no-async-promise-executor 禁止使用异步函数作为 Promise executor
no-compare-neg-zero 禁止与 -0 进行比较
no-cond-assign 禁止条件表达式中出现赋值操作符
no-constant-condition 禁止在条件中使用常量表达式
no-control-regex 禁止在正则表达式中使用控制字符
no-debugger 禁用 debugger
no-dupe-args 禁止 function 定义中出现重名参数
no-dupe-keys 禁止对象字面量中出现重复的 key
no-duplicate-case 禁止出现重复的 case 标签
no-empty 禁止出现空语句块
no-empty-character-class 禁止在正则表达式中使用空字符集
no-ex-assign 禁止对 catch 子句的参数重新赋值
no-extra-boolean-cast(#fix) 禁止不必要的布尔转换
no-extra-semi(#fix) 禁止不必要的分号
no-func-assign 禁止对 function 声明重新赋值
no-inner-declarations 禁止在嵌套的块中出现变量声明或 function 声明
no-invalid-regexp 禁止 RegExp 构造函数中存在无效的正则表达式字符串
no-irregular-whitespace 禁止不规则的空白
no-misleading-character-class 不允许在字符类语法中出现由多个代码点组成的字符
no-obj-calls 禁止把全局对象作为函数调用
no-prototype-builtins 禁止直接调用 Object.prototypes 的内置属性
no-regex-spaces(#fix) 禁止正则表达式字面量中出现多个空格
no-sparse-arrays 禁用稀疏数组
no-unexpected-multiline 禁止出现令人困惑的多行表达式
no-unreachable 禁止在 return、throw、continue 和 break 语句之后出现不可达代码
no-unsafe-finally 禁止在 finally 语句块中出现控制流语句
no-unsafe-negation(#fix) 禁止对关系运算符的左操作数使用否定操作符
require-atomic-updates 禁止由于 await 或 yield的使用而可能导致出现竞态条件的赋值
use-isnan 要求使用 isNaN() 检查 NaN
valid-typeof 强制 typeof 表达式与有效的字符串进行比较
no-case-declarations 不允许在 case 子句中使用词法声明
no-empty-pattern 禁止使用空解构模式
no-fallthrough 禁止 case 语句落空
no-global-assign 禁止对原生对象或只读的全局对象进行赋值
no-octal 禁用八进制字面量
no-redeclare 禁止多次声明同一变量
no-self-assign 禁止自我赋值
no-unused-labels 禁用出现未使用过的标
no-useless-catch 禁止不必要的 catch 子句
no-useless-escape 禁用不必要的转义字符
no-with 禁用 with 语句
no-delete-var 禁止删除变量
no-shadow-restricted-names 禁止将标识符定义为受限的名字
no-undef 禁用未声明的变量,除非它们在 /*global */ 注释中被提到
no-unused-vars 禁止出现未使用过的变量
no-mixed-spaces-and-tabs 禁止空格和 tab 的混合缩进
constructor-super 要求在构造函数中有 super() 的调用
no-class-assign 禁止修改类声明的变量
no-const-assign 禁止修改 const 声明的变量
no-dupe-class-members 禁止类成员中出现重复的名称
no-new-symbol 禁止 Symbolnew 操作符和 new 一起使用
no-this-before-super 禁止在构造函数中,在调用 super() 之前使用 this 或 super
require-yield 要求 generator 函数内有 yield
no-console 禁用 console
indent 强制使用一致的缩进
max-len 强制一行的最大长度
space-in-parens 强制在圆括号内使用一致的空格
curly 强制所有控制语句使用一致的括号风格
keyword-spacing 强制在关键字前后使用一致的空格
lines-around-comment 要求在注释周围有空行
one-var-declaration-per-line 要求或禁止在变量声明周围换行
comma-dangle 要求或禁止末尾逗号
quotes 强制使用一致的反勾号、双引号或单引号
no-nested-ternary 禁用嵌套的三元表达式
wrap-iife 要求 IIFE 使用括号括起来
no-new-wrappers 禁止对 String,Number 和 Boolean 使用 new 操作符
no-var 要求使用 let 或 const 而不是 var
no-duplicate-imports 禁止重复模块导入
eol-last 要求或禁止文件末尾存在空行
camelcase 强制使用骆驼拼写法命名约定

js中数组删除对象的几种方式总结

胖蔡阅读(227)

JS中数组是我们较为常用的一种数据结构,本篇文章主要是介绍js中我们常见的一些删除数组中元素的方式方法,希望可以对大家有所帮助。站长不易,感兴趣的小伙伴帮忙点个广告。

数组中元素的删除通过删除的方法的不同可以分为:

  • 关键字删除
  • splice删除
  • 特殊位置删除

 

关键字删除

关键字删除是通过js提供的关键字 delete手动删除数组的某一项。 使用delete删除掉数组中的元素后,会把该下标出的值置为undefined,数组的长度不会变。

var arr = ['a','b','c','d'];
delete arr[1];
arr;  
//["a", undefined × 1, "c", "d"] 中间出现两个逗号,数组长度不变,有一项为undefined

 

 

splice删除

splice方法是js中较为常用的数组方法,它不仅仅可以实现数组的删除,也可以实现数组的替换、新增等。通过splice方法删除的数组,数组会发生该表(长度索引也会发生改变)。这是较为常用的数组删除方法。使用删除的方法调用格式为:array.splice(index,len,[item]) 

 

//删除起始下标为1,长度为1的一个值(len设置1,如果为0,则数组不变)
var arr = ['a','b','c','d'];
arr.splice(1,1);
console.log(arr);  
//['a','c','d']; 

//删除起始下标为1,长度为2的一个值(len设置2)
var arr2 = ['a','b','c','d']
arr2.splice(1,2);
console.log(arr2); 
//['a','d']

 

特殊位置删除

当我们只是需要删除数组的首尾数据的时候,也可以通过js的pop或者shift方法实现,这其实是两对对应的操作:添加与删除。

 

  • pushpop

通过push我们可以将元素添加到数组末尾,通过pop将末尾元素去除并返回。

const arr = [1, 2, 3, 4, 5, 6]
// 添加元素到数组末尾
arr.push(7) // arr: [1, 2, 3, 4, 5, 6, 7]
// 取出数组末尾元素并返回
const pop_res = arr.pop() // arr: [1, 2, 3, 4, 5, 6], pop_res: 7
  • unshiftshift

通过unshift可以将元素添加到数组首部,通过shift可去除数组头部元素并返回。

// 将元素添加到数组开头
arr.unshift(0) // arr: [0, 1, 2, 3, 4, 5, 6]
// 取出数组开头元素并返回
const sft_res = arr.shift() // arr: [1, 2, 3, 4, 5, 6], sft_res = 0

 

 

 

 

使用node-xlsx读取生成excel文件

胖蔡阅读(175)

node-xlsx是基于js-xlsx的实现的excel解析、生成器。这里我们通过node-xlsx实现excel的读取和生成。

安装

$ npm install node-xlsx -S

 

解析 excel文件

import xlsx from 'node-xlsx';
// Or var xlsx = require('node-xlsx').default;

// Parse a buffer
const workSheetsFromBuffer = xlsx.parse(fs.readFileSync(`${__dirname}/myFile.xlsx`));
// Parse a file
const workSheetsFromFile = xlsx.parse(`${__dirname}/myFile.xlsx`);

 

生成excel文件

import xlsx from 'node-xlsx';
// Or var xlsx = require('node-xlsx').default;

const data = [[1, 2, 3], [true, false, null, 'sheetjs'], ['foo', 'bar', new Date('2014-02-19T14:30Z'), '0.3'], ['baz', null, 'qux']];
var buffer = xlsx.build([{name: "mySheetName", data: data}]); // Returns a buffer

 

自定义excel格式


import xlsx from 'node-xlsx';
 // Or var xlsx = require('node-xlsx').default; 
// 自定义列宽
const data = [[1, 2, 3], [true, false, null, 'sheetjs'], ['foo', 'bar', new Date('2014-02-19T14:30Z'), '0.3'], ['baz', null, 'qux']] 
const options = {'!cols': [{ wch: 6 }, { wch: 7 }, { wch: 10 }, { wch: 20 } ]}; 
var buffer = xlsx.build([{name: "mySheetName", data: data}], options); // Returns a buffer


// 跨行设置
const data = [[1, 2, 3], [true, false, null, 'sheetjs'], ['foo', 'bar', new Date('2014-02-19T14:30Z'), '0.3'], ['baz', null, 'qux']];
const range = {s: {c: 0, r:0 }, e: {c:0, r:3}}; // A1:A4
const options = {'!merges': [ range ]};
var buffer = xlsx.build([{name: "mySheetName", data: data}], options); // Returns a buffer


// 跨页合并单元格


const data = [[1, 2, 3], [true, false, null, 'sheetjs'], ['foo', 'bar', new Date('2014-02-19T14:30Z'), '0.3'], ['baz', null, 'qux']]; 
const range = {s: {c: 0, r:0 }, e: {c:0, r:3}}; // A1:A4 
const options = {'!merges': [ range ]}; 
var buffer = xlsx.build([{name: "mySheetName", data: data}], options); // Returns a buffer

 

 

Vue中是用downloadjs实现文件下载

胖蔡阅读(548)

1ed72c09f5e3fb0

Vue中使用downlaodjs库实现浏览器中文件的下载,可以防止通过a标签下载导致txt,jpg等直接展示不下载的问题。

1、downloadjs下载

$ npm i downloadjs -S

 

2、 Vue中使用

创建一个vue 页面组件。

 

<template>
  <div style="margin:20px">
     <button @click="downloadText">点击下载</button>
  </div>
</template>

<script>
import download from "downloadjs";
export default {
  methods: {
    download() {
       download(this.url);
    },
  },
data() {
  return {
      url:'/test.txt',
  };
 },
};
</script>


 

3、常用api

  • 纯文本下载成文件

download("hello world", "dlText.txt", "text/plain");

  • 通过文件的dataURL下载
download("data:text/plain,hello%20world", "dlDataUrlText.txt", "text/plain");
  • 通过blob下载
download(new Blob(["hello world"]), "dlTextBlob.txt", "text/plain");
  • 通过文件的地址
download("/robots.txt");
  • 文本 UInt8 数组
var str= "hello world",	arr= new Uint8Array(str.length);
str.split("").forEach(function(a,b){
  arr[b]=a.charCodeAt();
});

download( arr, "textUInt8Array.txt", "text/plain" );