1、获取地址栏的参数 URLSearchParams()

  1. var params = new URLSearchParams(location.search);
  2. var keys = params.getAll(name);

2、让指定元素滚动到浏览器的可视区域

  1. let inputDom = document.querySelector("input");
  2. inputDom.scrollIntoView() // 让当前的元素滚动到浏览器窗口的可视区域内。
  3. /**
  4. *还有一个方法是
  5. *用来将不在浏览器窗口的可见区域内的元素滚动到浏览器窗口的可见区域。
  6. */ 如果该元素已经在浏览器窗口的可见区域内,则不会发生滚动
  7. inputDom.scrollIntoViewIfNeeded()

3、将指定的字符串解析成html或者xml格式 element.insertAdjacentHTML

用法:
  1. document.querySelector(".contentBox").insertAdjacentHTML(position,htmlStr);
  2. // position是插入的位置;htmlStr是插入的字符串
  3. // position 取值范围:beforebegin(元素自身之前);
  4. // 'afterbegin'(插入元素内部的第一个子节点之前);
  5. // 'beforeend':插入元素内部的最后一个子节点之后。
  6. // 'afterend':元素自身的后面

4、Vue的props传值

因为父组件传值给子组件是单向数据流动,然后 Vue 官方不允许子组件直接更改 props 传递过来的值,
也就会报
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop’s value. Prop being mutated: “priceRuleName”
image.png
但是我们平常使用都是

  1. //父组件
  2. <Test :priceName="priceName"/>
  3. data(){
  4. return {
  5. priceName:"价格规则名称"
  6. }
  7. }
  8. // 子组件
  9. props:{
  10. priceName:{
  11. type:String,
  12. default:""
  13. }
  14. },
  15. watch:{
  16. priceName(){
  17. return this.$props.priceName
  18. }
  19. }
  20. <input v-model="priceName" />

当我们改变 **priceName** 的时候,就会改变 **props** 里的值,唯一的办法是,自己在子组件内自定义一个变量,然后将props里的值赋值给那个变量,页面也是直接绑定子组件的那个变量

  1. // 父组件
  2. <Test :priceName="priceName"/>
  3. //子组件
  4. <input v-model="newPriceName"/>
  5. data(){
  6. return {
  7. newPriceName:""
  8. }
  9. }
  10. watch:{
  11. priceName(){
  12. this.newPriceName = this.$props.priceName;
  13. return this.$props.priceName
  14. }
  15. }

5、比对两个Json的key 和value是否相同

  1. function comparedjson(json1, json2) {
  2. let isFlag = [];
  3. if ( Array.isArray(json1) && Array.isArray(json2)) {
  4. return json1.length == json2.length
  5. }
  6. //循环遍历其中一个json对
  7. for (var key in json1) {
  8. if (typeof (json1[key]) != "object" || json1[key] == null) {
  9. if (json2[key] != null) {
  10. if (json1[key] != json2[key]) {
  11. isFlag.push[{
  12. key: json1[key],
  13. key: json2[key]
  14. }]
  15. }
  16. } else {
  17. isFlag.push[{
  18. key: json1[key],
  19. key: json2[key]
  20. }]
  21. }
  22. } else {
  23. if (json1[key].length >= 0) {
  24. for (i = 0; i < json1[key].length; i++) {
  25. this.comparedjson(json1[key][i], json2[key][i]);
  26. }
  27. } else {
  28. this.comparedjson(json1[key], json2[key]);
  29. }
  30. }
  31. }
  32. return !!isFlag.length
  33. }

7、无刷新更新 URL 地址

详情:https://developer.mozilla.org/zh-CN/docs/Web/API/History/replaceState

  1. history.replaceState()

8、CSS 三角形生成器

链接:https://www.dute.org/css-arrow

使用 CSS 生成三角形是网页设计常见的需求,比起使用图片,CSS 生成的三角形具有更好的可控性和灵活性。本工具支持自定义三角形大小、颜色、方向、边框等属性。

image.png

9、正则验证特殊字符

  1. function vailitName(key, message) {
  2. const regEn = /[`~!@#$%^&*()_+<>?:"{},.\/;'[\]]/im,
  3. regCn = /[·!#¥(——):;“”‘、,|《。》?、【】[\]]/im
  4. if (regEn.test(key) || regCn.test(key)) {
  5. return message;
  6. }
  7. return message;
  8. },

10、对象循环引用导致 使用 JSON.stringify报错

问题描述:聊一聊JS中的循环引用及问题

报错信息:

  1. Converting circular structure to JSON
  2. --> starting at object with constructor 'Array'
  3. | index 0 -> object with constructor 'Object'
  4. --- property 'secondMenuList' closes the circle

举个例子:

  1. const test = {a:"2020年11月25日16:28:35"}
  2. test.b = test;
  3. const item = JSON.stringify(test);

运行以上问题就会提示

  1. Converting circular structure to JSON
  2. --> starting at object with constructor 'Array'
  3. | index 0 -> object with constructor 'Object'
  4. --- property 'b' closes the circle

原因:序列化的对象存在循环引用的情况,使得内存溢出导致报错
解决:在序列化的时候中断引用就可以 JSON.stringify 就提供了方法
语法:

  1. JSON.stringify(value[, replacer[, space]])

完整的例子:

  1. const getCircularReplacer = () => {
  2. const seen = new WeakSet();
  3. return (key, value) => {
  4. if (typeof value === "object" && value !== null) {
  5. if (seen.has(value)) {
  6. return;
  7. }
  8. seen.add(value);
  9. }
  10. return value;
  11. };
  12. }
  13. JSON.stringify(test, getCircularReplacer());

11、如何使用 JS 控制全屏?

1. 进入全屏
  1. /**
  2. * element 需要进入全屏的元素
  3. */
  4. function requestFullScreen(element) {
  5. var requestMethod = element.requestFullScreen || //W3C
  6. element.webkitRequestFullScreen || //Chrome等
  7. element.mozRequestFullScreen || //FireFox
  8. element.msRequestFullScreen; //IE11
  9. if (requestMethod) {
  10. requestMethod.call(element);
  11. }else if (typeof window.ActiveXObject !== "undefined") {//for Internet Explorer
  12. var wscript = new ActiveXObject("WScript.Shell");
  13. if (wscript !== null) {
  14. wscript.SendKeys("{F11}");
  15. }
  16. }
  17. }

2. 退出全屏
  1. //退出全屏
  2. function exitFull() {
  3. var exitMethod = document.exitFullscreen || //W3C
  4. document.mozCancelFullScreen || //Chrome等
  5. document.webkitExitFullscreen || //FireFox
  6. document.webkitExitFullscreen; //IE11
  7. if (exitMethod) {
  8. exitMethod.call(document);
  9. }else if (typeof window.ActiveXObject !== "undefined") {//for Internet Explorer
  10. var wscript = new ActiveXObject("WScript.Shell");
  11. if (wscript !== null) {
  12. wscript.SendKeys("{F11}");
  13. }
  14. }
  15. };

12、number 数字类型的格式化

可使用 Intl.NumberFormat 实现

Intl.NumberFormat 是对语言敏感的格式化数字类的构造器类

实现的功能例如:

  • 数字使用 千位分隔符 ```javascript const number = 123456789;

// 德语使用逗号作为小数点,使用.作为千位分隔符 console.log(new Intl.NumberFormat(‘en-US’).format(number)); // 123,456,789

  1. - 数字 实现金额转化
  2. ```javascript
  3. const number = 123456789;
  4. new Intl.NumberFormat('ja-JP', { style: 'currency', currency: 'JPY' }).format(number)
  5. // → ¥123,456,789;
  • 数字转成 中文十进制数字
    1. const number = 123456789;
    2. new Intl.NumberFormat('zh-Hans-CN-u-nu-hanidec').format(number);
    3. // → 一二三,四五六,七八九

14、一秒让页面的文字编程可编辑状态

  1. // 进入编辑页面
  2. document.designMode ="on"
  3. // 退出编辑状态
  4. document.designMode = "off"

15 JavaScript实现禁止打开控制台

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Document</title>
  7. <style type="text/css">
  8. /*禁止选中文字*/
  9. body {
  10. -moz-user-select: none;
  11. /*火狐*/
  12. -webkit-user-select: none;
  13. /*webkit浏览器*/
  14. -ms-user-select: none;
  15. /*IE10*/
  16. -khtml-user-select: none;
  17. /*早期浏览器*/
  18. user-select: none;
  19. }
  20. </style>
  21. </head>
  22. <body>
  23. <script type='text/javascript'>
  24. //禁用右键(防止右键查看源代码)
  25. window.oncontextmenu = function () { return false; }
  26. //禁止任何键盘敲击事件(防止F12和shift+ctrl+i调起开发者工具)
  27. window.onkeydown = window.onkeyup = window.onkeypress = function () {
  28. window.event.returnValue = false;
  29. return false;
  30. }
  31. //如果用户在工具栏调起开发者工具,那么判断浏览器的可视高度和可视宽度是否有改变,如有改变则关闭本页面
  32. var h = window.innerHeight, w = window.innerWidth;
  33. window.onresize = function () {
  34. if (h != window.innerHeight || w != window.innerWidth) {
  35. window.close();
  36. window.location = "about:blank";
  37. }
  38. }
  39. /*好吧,你的开发者工具是单独的窗口显示,不会改变原来网页的高度和宽度, 但是你只要修改页面元素我就重新加载一次数据,让你无法修改页面元素(不支持IE9以下浏览器)*/
  40. if (window.addEventListener) {
  41. window.addEventListener("DOMCharacterDataModified", function () { window.location.reload(); }, true);
  42. window.addEventListener("DOMAttributeNameChanged", function () { window.location.reload(); }, true);
  43. window.addEventListener("DOMCharacterDataModified", function () { window.location.reload(); }, true);
  44. window.addEventListener("DOMElementNameChanged", function () { window.location.reload(); }, true);
  45. window.addEventListener("DOMNodeInserted", function () { window.location.reload(); }, true);
  46. window.addEventListener("DOMNodeInsertedIntoDocument", function () { window.location.reload(); }, true);
  47. window.addEventListener("DOMNodeRemoved", function () { window.location.reload(); }, true);
  48. window.addEventListener("DOMNodeRemovedFromDocument", function () { window.location.reload(); }, true);
  49. window.addEventListener("DOMSubtreeModified", function () { window.location.reload(); }, true);
  50. }
  51. </script>
  52. </body>
  53. </html>

插件

一、前端将word文档转成 HTML

1、Mammoth.js 简介

Mammoth.js 旨在转换 .docx 文档(例如由 Microsoft Word 创建的文档),并将其转换为 HTML。
【Mammoth 的目标是通过使用文档中的语义信息并忽略其他细节来生成简单干净的 HTML。】 比如,Mammoth 会将应用标题 1 样式的任何段落转换为 h1 元素,而不是尝试完全复制标题的样式(字体,文本大小,颜色等)。

由于 .docx 使用的结构与 HTML 的结构之间存在很大的不匹配,这意味着对于较复杂的文档而言,这种转换不太可能是完美的。但如果你仅使用样式在语义上标记文档,则 Mammoth 能实现较好的转换效果。
当前 Mammoth 支持以下主要特性:

  • Headings
  • Lists,Table
  • Images
  • Bold, italics, underlines, strikethrough, superscript and subscript
  • Links,Line breaks
  • Footnotes and endnotes

它还支持自定义映射规则。例如,你可以通过提供适当的样式映射将 WarningHeading 转换为 h1.warning。另外文本框的内容被视为单独的段落,出现在包含文本框的段落之后。

Mammoth.js 这个库为我们提供了很多方法,这里我们来介绍三个比较常用的 API:

mammoth.convertToHtml(input, options):把源文档转换为 HTML 文档
mammoth.convertToMarkdown(input, options):把源文档转换为 Markdown 文档。这个方法与 convertToHtml 方法类似,区别就是返回的 result 对象的 value 属性是 Markdown 而不是 HTML。
mammoth.extractRawText(input):提取文档的原始文本。这将忽略文档中的所有格式。每个段落后跟两个换行符。
介绍完 Mammoth.js 相关的特性和 API,接下来我们开始进入实战环节。

2、 Mammoth.js 实战

Mammoth.js 这个库同时支持 Node.js 和浏览器两个平台,在浏览器端 mammoth.convertToHtml 方法的 input 参数的格式是 {arrayBuffer: arrayBuffer},其中 arrayBuffer 就是 .docx 文件的内容。在前端我们可以通过 FileReader API 来读取文件的内容,此外该接口也提供了 readAsArrayBuffer 方法,用于读取指定的 Blob 中的内容,一旦读取完成,result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象。下面我们定义一个 readFileInputEventAsArrayBuffer 方法:

export function readFileInputEventAsArrayBuffer(event, callback) {
  const file = event.target.files[0];
  const reader = new FileReader();
  reader.onload = function(loadEvent: Event) {
          const arrayBuffer = loadEvent.target["result"];
          callback(arrayBuffer);
  };
  reader.readAsArrayBuffer(file);
}

该方法用于实现把输入的 File 对象转换为 ArrayBuffer 对象。在获取 Word 文档对应的 ArrayBuffer 对象之后,就可以调用 convertToHtml 方法,把 Word 文档内容转换为 HTML 文档。

mammoth.convertToHtml({ arrayBuffer })

此时如果你的文档中不包括特殊的图片类型,比如 wmf 或 emf 类型,而是常见的 jpg 或 png 等类型的话,那么你可以看到 Word 文档中的图片。难道这样就搞定了,那是不是太简单了,其实这只是个开始。当你通过浏览器的开发者工具审查 Word 解析后的 HTML 文档后,会发现图片都以 Base64 的格式进行嵌入。如果图片不多且单张图片也不会太大的话,那这种方案是可以考虑的。

针对多图或大图的情况,一种比较好的方案是把图片提交到文件资源服务器上。在 Mammoth.js 中要实现上述的功能,可以使用 「convertImage」 配置选项来自定义图片处理器。具体的使用示例如下:

let options = {
        convertImage: mammoth.images.imgElement(function(image) {
        return image.read("base64").then(function(imageBuffer) {
            return {
              src: "data:" + image.contentType + ";base64," + imageBuffer
            };
        });
    })
};

以上示例实现的功能就是把 Word 中的图片进行 Base64 编码,然后转成 Data URL 的形式,以实现图片的显示。很明显这不符合我们的要求,所以我们需要做以下调整:

const mammothOptions = {
        convertImage: mammoth.images.imgElement(function(image) {
    return image.read("base64").then(async (imageBuffer) => {
      const result = await uploadBase64Image(imageBuffer, image.contentType);
            return {
                src: result.data.path // 获取图片线上的URL地址
          };
      });
     })
};

顾名思义 uploadBase64Image 方法的作用就是上传 Base64 编码后的图片:

async function uploadBase64Image(base64Image, mime) {
        const formData = new FormData();
        formData.append("file", base64ToBlob(base64Image, mime));
        return await axios({
                method: "post",
                url: "http://localhost:3000/uploadfile", // 本地图片上传的API地址
                data: formData,
                config: { headers: { "Content-Type": "multipart/form-data" } }
        });
}

为了减少图片文件的大小,我们需要把 Base64 格式的图片先转成 Blob 对象,然后在通过创建 FormData 对象进行提交。base64ToBlob 方法的定义如下:

function base64ToBlob(base64, mimeType) {
    let bytes = window.atob(base64);
    let ab = new ArrayBuffer(bytes.length);
    let ia = new Uint8Array(ab);
    for (let i = 0; i < bytes.length; i++) {
        ia[i] = bytes.charCodeAt(i);
    }
    return new Blob([ia], { type: mimeType });
}

这时把 Word 文档转换为 HTML 并自动把 Word 文档中的图片上传至文件资源服务器的基本功能已经实现了。对于 Mammoth.js 内部是如何解析 Word 中的 XML 文件,我们就不做介绍了,反之我们来简单介绍一下 Mammoth.js 内部依赖的 JSZip 这个库。

3、 JSZip 简介

JSZip 是一个用于创建、读取和编辑 「.zip」 文件的 JavaScript 库,含有可爱而简单的 API。该库的兼容性如下所示:
image.png
经过最新版本的测试 经过 3.0/3.6/最新版本测试 经过最新版本的测试 经过最新版本的测试 经过 IE 6 / 7 / 8 / 9 / 10 测试 经过 Node.js 0.10 / 最新版本测试

4、 JSZip 安装

使用 JSZip 时,你可以通过以下几种方式进行安装:

//「npm」:
npm install jszip
//「bower」:
bower install Stuk/jszip
// 「component」 :
component install Stuk/jszip

「手动」:先下载 JSZip 安装包,然后引入 dist/jszip.js 或 dist/jszip.min.js 文件

5、JSZip 使用示例
let zip = new JSZip();
zip.file("Hello.txt", "Hello Semlinker\n");
let img = zip.folder("images");
img.file("smile.gif", imgData, {base64: true});
zip.generateAsync({type: "blob"}).then(function(content) {
        // see FileSaver.js
        saveAs(content, "example.zip");
});

该示例来自 JSZip 官网,成功运行之后,会自动下载并保存 「example.zip」 文件。该文件解压后的目录结构如下所示:

二、Word 文档转换成 Markdown 文档

「Markdown 是一种轻量级标记语言」 ,创始人为约翰·格鲁伯(英语:John Gruber)。它允许人们使用易读易写的纯文本格式编写文档,然后转换成有效的 XHTML(或者 HTML)文档。这种语言吸收了很多在电子邮件中已有的纯文本标记的特性。

由于 Markdown 的轻量化、易读易写特性,并且对于图片,图表、数学式都有支持,目前许多网站都广泛使用 Markdown 来撰写帮助文档或是用于论坛上发表消息。

了解完 Markdown 是什么之后,我们来分析一下如何把 Word 文档转换成 Markdown 文档。对于这个功能,我们也有两种处理方式:

第一种:使用 Mammoth.js 这个库提供的 mammoth.convertToMarkdown(input, options) 方法;
第二种:基于 mammoth.convertToHtml(input, options) 生成的 HTML 文档,在利用 HTML to Markdown 的转换工具,来间接实现上述功能。
下面我们来介绍第二种方案,这里我们使用 Github 上一个开源的转换器 —— turndown,它是使用 JavaScript 开发的 HTML to Markdown 转换器,使用起来很简单。

首先你可以通过以下两种方式来安装它:

npm install turndown

安装完之后,你就可以通过调用 TurndownService 构造函数,来创建 turndownService 实例,然后调用该实例的 turndown() 方法执行转换操作:

let markdown = turndownService.turndown(
        document.getElementById('content')
)

对于前面使用的 「abao.docx」 文档,最终转换生成的 Markdown
需要注意的是,TurndownService 构造函数支持很多配置项,这里阿宝哥就不详细介绍了。感兴趣的小伙伴,可以自行阅读 turndown 官方文档或访问 turndown 在线示例 实际体验一下。

既然已经讲到 Markdown,阿宝哥再给小伙伴们介绍一个 Github 上不错的开源库 markmap,该库使用思维导图的方式来实现 Markdown 文档的可视化,整体效果还蛮不错的:

(图片来源:https://markmap.js.org/repl/

最后,我们再来看一下在前端如何动态生成 Word 文档。

四、前端动态生成 Word 文档

在前端如果要动态生成 Word 文档,我们可以直接利用一些成熟的第三方开源库,比如:docx 或 html-docx-js。

下面我们将以 docx 为例,来介绍如何在前端如何生成 「.docx」 格式的 Word 文档。Docx 这个库提供了优雅的声明式 API,让我们可以使用 JS/TS 轻松生成 .docx 文件。此外,它还同时支持 **Node.js** 和浏览器。

**Docx.js** 这个库为开发者提供了许多类,用于创建 Word 中的对应元素,这里我们简单介绍几个常见的类:

{
    Document:''  , //用于创建新的 Word 文档;
    Paragraph:'' ,  //用于创建新的段落;
    TextRun:'',    //用于创建文本,支持设置加粗、斜体和下划线样式;
    Tables:'',   //用于创建表格,支持设置表格每一行和每个表格单元的内容。
}

接下来阿宝哥将使用 Docx 这个库,来动态生成前面介绍过的 「abao.docx」 文档,具体代码如下所示:

阿宝哥 - 动态生成 Word 文档示例
<button type="button" onclick="generate()">
  点击生成 Docx 文档
</button>
<script src="https://unpkg.com/docx@5.0.2/build/index.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.js"></script>
<script>
  async function generate() {
    const doc = new docx.Document();

    const imageBuffer = await fetch(
      "https://avatars3.githubusercontent.com/u/4220799"
    ).then((response) => response.arrayBuffer());

    const image = docx.Media.addImage(doc, imageBuffer, 230, 230);

    doc.addSection({
      properties: {},
      children: [
        new docx.Paragraph({
          children: [
            new docx.TextRun({
              text: "全栈修仙之路,",
              bold: true,
            }),
            new docx.TextRun({
              text:
                "聚焦全栈,专注分享 TypeScript、Web API、Node.js、Deno 等全栈干货。",
            }),
          ],
        }),
        new docx.Paragraph(image),
      ],
    });

    docx.Packer.toBlob(doc).then((blob) => {
      console.log(blob);
      saveAs(blob, "abao.docx");
      console.log("文档生成成功");
    });
  }
</script>

接着我们会调用 **doc.addSection()** 方法来添加 **Section** 块,该块将作为段落的容器。在示例中,我们创建的 **Section** 块包含两个段落,一个用于存放文本信息,而另一个用于存放图片信息。最后我们会把 **Document** 对象转换成 **Blob** 对象,然后通过 **saveAs()** 方法下载到本地。