LOADING

加载过慢请开启缓存 浏览器默认开启

耀耀切克闹

前端打印功能的实现

组件 2023/1/14

前端打印功能的实现

1.安装与使用

Vue2 Version:

npm install vue-print-nb --save

// Global instruction  main.js中全局引入
import Print from 'vue-print-nb'
Vue.use(Print);

//or
// Local instruction
import print from 'vue-print-nb'

directives: {
    print   
}

Vue3 Version:

npm install vue3-print-nb --save
// Global instruction 
import { createApp } from 'vue'
import App from './App.vue'
import print from 'vue3-print-nb'
const app = createApp(App)
app.use(print)
app.mount('#app')

//or

// Local instruction
import print from 'vue3-print-nb'

directives: {
    print   
}

基本使用

<div id="printTest">    
    打印测试 </div> 
<el-button v-print="'#printTest'">打印</el-button>

2.页眉页脚字符串问题

打印预览时发现页眉页脚会有页码 网址 undefined之类的字符

  1. 直接利用样式解决,优点是简单粗暴,缺点是页面的上下边距都不存在了,就没有页面的上下留白了,看着打印出来的内容上下边距太窄 像下图设置为3mm就隐藏掉了页眉页脚
// 打印媒体查询
@media print {
  @page{
      size:  auto;
      margin: 3mm;
    }    
}

3.打印时内容显示不全问题

在实际页面中高度不够部分内容隐藏了,这时候需要将对应块的内容为隐藏滚动的内容显示为全显示,打印时隐藏的内容并不会打印,只会打印到页面直接展示出来的内容

// 打印媒体查询
@media print {
  @page{
      size:  auto;
      margin: 3mm;
    }
 body{ 
        height:auto;  //在实际页面中高度不够部分内容隐藏了
  }       
}

4.打印时样式消失了

样式消失了的根本原因是写在打印内容外层的样式以及使用的一些框架的样式没有 在打印时 拿到的实际是打印块的iframe内容,如果明知道这块的内容需要打印,表格或者块内容不要使用任何UI框架以及其它外部scss或者less的内容,外部的类名拿不到,里面的所有样式拿不到, 要么单独根据api写上引入的样式地址,直方便便捷的方式是内联样式或者是写内部样式

 <div class="box">
    <div id="printTest">
      <div class="print-style">打印的相关内容</div>
    <div>       
 </div>
  <style lang="scss" scoped>
    .box { } //无效
    #printTest { } //无效
     .print-style { } //写在这里的有效果
  </style>

5.打印时需要根据内容分页及内容添加水印

打印时如果有多个表格或者多个内容块,可以通过计算进行分以及给每一页添加水印 实际情况根据设置按需调整

<div id="printTest">
    <div class="printPage printMark"  v-for="item in 5" :key="item" >
      打印的相关内容
    </div>
 <div>     

// 打印媒体查询
@media print {
  @page{
      size:  auto;
      margin: 3mm;
   }
  body{ 
    height:auto; 
  }
  .printPage {   
    height: 291mm;   //分页  根据实际
    position: relative; 
    &::before {
        position: absolute; content: 'xx水印'; top:20%; right: 5%;  font-size: 48px; opacity: 0.1;  transform: rotate(30deg);
    }
    &::after {
        position: absolute; content: 'xx水印'; bottom:20%; left: 5%;  font-size: 48px; opacity: 0.1;  transform: rotate(30deg);
    }
  }    
}

参考文章:

vue-print-nb · GitHub Topics

vue 打印 vue-print-nb - 简书 (jianshu.com)

阅读全文

在开发h5移动端项目(v3+vant3+pinia+vite)时遇到的问题总结

前端随笔 2022/12/30
-->

一.移动端适配和适配后内联样式没有生效

移动端适配

既然是整移动端项目,那么移动端适配是必须要整了,目前开发的这个项目就是使用的是rem方案,rem方案的话比较常用的插件就是postcss-pxtorem+lib-flexible,至于为什么没用vw方案, vw 方案 还是有缺点的。如 vw 方案不适合大屏,因为 vw 是一个比例单位,随着屏幕尺寸变大,使用vw单位的元素、字体也越来越大。但我们肯定是希望在大屏上展示更多的内容,而不是更大的文字、图标。现在用大屏的用户也是挺多的,我自己感觉还是rem方案好一点点(手动狗头)

yarn add amfe-flexible
yarn add -D postcss-pxtorem

根目录下postcss.config.cjs

module.exports = {
    plugins: {
      'postcss-pxtorem': {
        //如果你蓝湖上的UI图尺寸为375px,这里rootValue设置为37.5
        //如果你蓝湖上的UI图尺寸为750px,这里rootValue设置为75
        //蓝湖上的UI图的尺寸可以调节的,移动端一般用的37.5
        rootValue: 37.5,
        propList: ['*'],
      },
    },
};

main.ts中

import 'amfe-flexible';

内联样式没有生效

整完这些后,开发着开发着,用vant样式没事,自己写的样式也嘎嘎生效,突然有个dom元素需要加个动态样式,或者图片需要设置宽高,这就需要使用到内联样式,然后就发现了设置的内联样式不生效,弔!

解决方法也挺简单,就是在main.ts中定义一个全局方法,用来把px转化成rem:
main.ts中加上

const px2rem = (px:any) => {
    if(/%/ig.test(px)){ // 有百分号%,特殊处理,表述pc是一个有百分号的数,比如:90%
      return px
    }else{
      return (parseFloat(px) / 37.5) + 'rem'
    }
  }
app.config.globalProperties.$px2rem = px2rem // 放到全局

使用:

 <div :style="{marginBottom:(ismarginBottom? $px2rem('42px'):$px2rem('16px'))}">
 </div>

二.H5 IOS input 聚焦时,页面整个被推上去了,键盘收起页面未下移 BUG

遇见的场景

vue基于vant3框架开发移动端项目时,出现了这样一个问题:在手机上点击页面输入框时唤起手机自带键盘,结果顶部固定的导航栏也被顶起,导致遮挡住页面部分内容

解决方式

给相应的输入框一个聚焦的事件:

  <van-field
     v-model="cardForm.topUpamount"
     label="¥"
     :rules="rulesFrom.amount"
     placeholder=""
     :clearable="true"
     type="number"
     @focus="changefocus"
  >

这个可以放在utils的index.ts下,导出这个方法,在需要的页面使用

如果这个输入框不是在vant组件的弹窗:

 // ios键盘唤起,键盘收起以后页面不归位bug解决
 const changefocus = () => {
    const u = navigator.userAgent
    const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
      if(isIOS){
        setTimeout(() => {
          window.scrollTo(0, 0)
          }, 200)
      }
  }

如果这个输入框是在vant组件的弹窗,例如动作面板弹窗:

  // ios键盘唤起,键盘收起以后页面不归位bug解决
  const changefocus = () => {
    const u = navigator.userAgent;
    const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
    if (isIOS) {
      setTimeout(() => {
        document.body.scrollTop = document.body.scrollHeight;
      }, 200);
    }
  };
  const changeblur = () => {
    const u = navigator.userAgent;
    const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
    if (isIOS) {
      document.body.scrollTop = 0;
      window.scrollTo(0, 0);
    }
  };

三.状态调接口轮询

如充值,提现这种场景时,三方那边返回的结果可能需要比较久,这个时候就需要轮询着去结果,根据轮询的结果展示不同的需要展示的页面,如充值中,充值成功,失败等

const timer: any = ref(null);
  const Result = async (No:any) => {
    try {
      const res: any = await getResult({No: tNo })
      // 0 处理中 1 成功 2失败
      if ([0].includes(res.data.State)) {
        timer.value = setTimeout(() => {
          Result(No);
        }, 2 * 1000);
      } else if (res.data.State === 1 || res.data.State === 2) {
        clearTimeout(timer.value);
      }
      State.value = res.data.State
      //该赋值的赋值
      //......
    } catch (error) {
      clearTimeout(timer.value);
    }
  };

离开页面时,一定要清除定时,避免一直调用接口

  onBeforeUnmount(() => {
    clearTimeout(timer.value);
  })

四.第一次进入页面有在app.vue的onmouted里写了获取token方法调接口,页面刷新后token没了

使用pinia+数据持久化,存放token 或者直接把接口获取到的存在localstorage

五.app端嵌套h5页面时,返回APP端时,不能使用router.go(-1),需要使用app端提供的关闭的桥的方法

isIos.js

export const isIos = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)

bridge.js

import {isIos } from './isIos'
 
const setupWebViewJavascriptBridge = (callback) => {
    if (isIos) {
        if (window.WebViewJavascriptBridge) return callback(window.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(() => { document.documentElement.removeChild(WVJBIframe) }, 0)
    }
}

export const appBridge = (apiName, payload, cb) => {
    if (isIos) {
        setupWebViewJavascriptBridge((bridge) => bridge.callHandler(apiName, payload, cb))
    } else {
        let res = null
        if (window.jsHZG[apiName]) {
            if (JSON.stringify(payload) !== '{}' && payload) {
                if (typeof payload === 'object') {
                    res = window.jsHZG[apiName](JSON.stringify(payload))
                } else {
                    res = window.jsHZG[apiName](payload)
                }
            } else {
                res = window.jsHZG[apiName]()
            }
            if (cb) cb(res)
        }
    }
}

export const registerhandler = (name, callback) => {
    if (isIos) {
        setupWebViewJavascriptBridge(function (bridge) {
            bridge.registerHandler(name, callback)
        })
    } else {
        window[name] = data => callback(data)
    }
}

webView.js

import { appBridge } from './bridge'
// 打开新web-view 
export const newPage = (isFull, path, query) => {
    return appBridge('openNewPage', { url: isFull ? path: `${window.location.origin}${path}`  })
}

// 关闭当前web-view
export const closePage = () => {
    return appBridge('closePage')
}

//跳转小程序

export const openXCX = () => {
    return appBridge('payStagesOrder')
}

// 获取原生导航条的高度

export const getStatusBarHeight = () => {
    return appBridge('getStatusBarHeight')
}

使用:

import { closePage} from '@/utils/webView'
//在需要的地方
closePage()

六. 项目打包发布后,前端静态图片在测试环境无法显示的问题

首先我们去vite官网看看关于静态资源文件的处理:

企业微信截图_16720467796522.png
image.png
这里我们先假设:
静态文件目录:src/assets/images/
我们的目标静态文件在 src/assets/images/home/home_icon.png

<img src="src/assets/images/home/home_icon.png" />

那么这么写就会出现问题,在本地环境时你能够显示出图片,但打包发布到测试环境问题就出来了,图片没显示出来。
第一张图片标红的位置打包后是: /assets/images/home/home_icon.(hash的值).png

综上所得:如果资源文件在assets文件夹打包后会把图片名加上 hash值,但是直接通过 :src=”imgSrc”方式引入并不会在打包的时候解析,导致开发环境可以正常引入,打包后却不能显示的问题
所以:<img src="src/assets/images/home/home_icon.png" /> 是肯定找不到的。

1.第一种方式解决方式(适用于处理单个链接的资源文件 普通推荐!!!!)

import homeIcon from '@/assets/images/home/home_icon.png'
<img :src="homeIcon" />

2.第二种解决方式:new URL() + import.meta.url(适用于处理多个链接的资源文件 极力推荐!!!!)

企业微信截图_16720474775759.png
这也是vite官方文档说明的方式,new URL() + import.meta.url
工具文件目录: src/util/img-use.ts
img-use.ts:

// 获取assets静态资源
export const getAssetsFile = (url: string) => {
   return new URL(`../assets/images/${url}`, import.meta.url).href
}

使用:

import getAssetsFile from '@/util/img-use'
//setup语法糖写法,没用语法糖的记得return出去
//如果是直接建在assets/images下,写图标名就行了
<img :src="getAssetsFile('home_icon.png')" />
//可以在../assets/images建目录如home文件夹,那么就是
<img :src="getAssetsFile('/home/home_icon.png')" />

另外:如果是背景图片引入的方式(一定要使用相对路径)

.imgBg {
  background-image: url('../../assets/images/WDNMD.jpg');
}

3.第三种解决方式import.meta.glob或import.meta.globEager(适用于处理多个链接的资源文件 不怎么推荐!!!!)

这种方式引入的文件必须指定到具体文件夹路径,传入的变量中只能为文件名,不能包含文件路径

使用vite的import.meta.glob或import.meta.globEager,两者的区别是前者懒加载资源,后者直接引入。
那我为什么说不怎么推荐了,如果你使用的是最新版本的vite,就会出现

image.png
可以看出import.meta.globEager已被弃用,只能用import.meta.glob了,并且你只能传图片名,不能传路径。

const modules = import.meta.glob(‘./menus/**/*.ts’, { eager: true,import:’default’ })
以“default”这个选项作为默认的导出内容,从而避免对象为’unknow’时获取default的报错

工具文件目录: src/util/img-use.ts
img-use.ts:

// 获取assets静态资源
export const getAssetsHomeFile = (url: string) => {
    const path = `../assets/images/home/${url}`;
    const modules = import.meta.globEager("../assets/images/home/*");
    //const modules = import.meta.globEager("../assets/images/home/*", { eager: true,import:'default' });
    return modules[path].default;
}

使用:

import useImg from '@/util/img-use'
//写图标名就行了,不能带路径,并且你只能传图片名,不能传路径。
<img :src="useImg('home_icon.png')" />

七.h5移动端,安卓看着没问题,ios的手机文字嘎嘎换行

(原因:给对应的dom元素设置了宽度,导致了换行,移动端布局时非必要不要设置宽度)

八.vant3 Dialog组件title没显示,我的是小米11的,ios或安卓的其他手机都显示了

目前没找到原因,见鬼了,就我的手机 Dialog组件title没显示。

//手机号弹窗提示
  const dialogShow = () => {
    Dialog.alert({
      confirmButtonText: '我知道了',
      title: '你瞅啥', //这个没显示,离谱
      confirmButtonColor: '#FE7E41',
      message: '瞅你乍地!!',
    }).then(() => {});
  };

九.小程序嵌套web-view H5页时,出现双导航的情况

在app.json中,小程序navBarTitle不设置,并且h5项目的mian.ts调用接口,获取这个是从哪来到或嵌套该h5页,把来源存放在全局中,用来判断是否隐藏left-arrow 返回箭头

获取:

//调接口获取来源.............. 得到sourcePage
     
app.config.globalProperties.$sourcePage = sourcePage // 放到全局

使用:

  <van-nav-bar
    title="我的"
    :left-arrow="$sourcePage != '来源的值'"
    :border="false"
    style="width: 10rem"
    :placeholder="true"
    :fixed="true"
    @click-left="onClickLeft"
  />

这样就不是双导航了

十.app端嵌套web-view H5页时,出现双导航的情况

让app端把原生的导航给隐藏了,使用h5的导航

十一.h5 title为空时,安卓手机导航栏会默认展示h5域名。

解决方案:通过document.title改变h5 title

十二.安卓端布局正常,两div上下有间距,ios没间距

ios自己写的样式不是vant组件的(vant组件自己做了适配)文字需要高度和行高,没高度就挤在一起了

总结一手:vant组件使用样式穿透时,就不要给宽度,高度行高了,vant组件组件整好适配了,自己写的样式特别时文字需要给高度和行高,按蓝湖上来就行了

十三.微信小程序配置业务域名,调用web-view组件打开需要嵌套的h5页面

应为有其他的微信 去到微信公众平台,开发管理下的开发设置的下配置义务域名
1672708788778.png

下载的校验文件,放在public下,并且不要重命名,是什么名字就是什么,不然检测不到

阅读全文

浏览器相关原理

前端随笔 2022/12/20

前言:
了解浏览器相关原理,自己也在掘金,csdn,知乎上看了别人的文章,觉得部分内容对我有益,复制下来做了下笔记,如有雷同,纯属c+v(手动狗头)

一、浏览器种类

  • IE (internet Explorer)浏览器
  • Firefox 火狐浏览器
  • Chrome 谷歌浏览器
  • Safari 浏览器
  • Opera 浏览器

谈谈几种浏览器的内核

  • 内核只是一个通俗的说法,其英文名称为“Layout engine”,翻译过来就是“排版引擎”,也被称为“页面渲染引擎”

  • 它负责取得网页的内容(HTML、XML、图像等等)、整理信息(例如加入CSS等),以及计算网页的显示方式,然后会输出至显示器或打印机

  • 所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要排版引擎。

  • 内核种类

    • Trident

      • 这是微软开发的一种排版引擎。
      • 有时候,为了方便也有很多人直接简称其为IE内核
      • IE、Maxthon、TT、The World
    • Geckos

      • Gecko是套开放源代码的、以C++编写的网页排版引擎。
      • FireFox
    • Presto

      • Presto是一个由Opera Software开发的浏览器排版引擎
      • Opera
    • Webkit

      • 苹果公司自己的内核,也是苹果的Safari浏览器使用的内核
      • Webkit引擎包含WebCore排版引擎及JavaScriptCore解析引擎
      • Safari、Chrome

二、浏览器功能

  • 浏览器功能:

    • 1、向服务器发出请求
    • 2、在浏览器窗口中展示您选择的网络资源
    • 这里所说的资源一般是指HTML文档,PDF、图片、其他的类型。
    • 资源的位置:由用户使用 URI(统一资源标示符)指定。
  • 浏览器解释显示规范:

    • 浏览器解释并显示 HTML 文件的方式是在 HTML 和 CSS 规范中指定的。
    • 这些规范由网络标准化组织 W3C(万维网联盟)进行维护。
    • 万恶之源:虽然制定了相应的规范,但是各浏览器都没有完全遵从这些规范,同时还在开发自己独有的扩展程序,这给网络开发人员带来了严重的兼容性问题。
  • 浏览器的界面用户元素

    • 用来输入 URI 的地址栏
    • 前进和后退按钮
    • 书签设置选项
    • 用于刷新和停止加载当前文档的刷新和停止按钮
    • 用于返回主页的主页按钮
  • 浏览器的用户界面并没有任何正式的规范,这是多年来的最佳实践自然发展以及彼此之间相互模仿的结果。

三、浏览器渲染过程是怎样的?

按照渲染的时间顺序,流水线可分为如下几个子阶段:构建 DOM 树、样式计算、布局阶段、分层、栅格化和显示。

  1. 渲染进程将 HTML 内容转换为能够读懂DOM 树结构。
  2. 渲染引擎将 CSS 样式表转化为浏览器可以理解的styleSheets,计算出 DOM 节点的样式。
  3. 创建布局树,并计算元素的布局信息。
  4. 对布局树进行分层,并生成分层树。
  5. 为每个图层生成绘制列表,并将其提交到合成线程。合成线程将图层分图块,并栅格化将图块转换成位图。
  6. 合成线程发送绘制图块命令给浏览器进程。浏览器进程根据指令生成页面,并显示到显示器上。

浏览器从网络或硬盘中获得HTML字节数据后会经过一个流程将字节解析为DOM树,先将HTML的原始字节数据转换为文件指定编码的字符,然后浏览器会根据HTML规范来将字符串转换成各种令牌标签,如html、body等。最终解析成一个树状的对象模型,就是dom树;

获取css,获取style标签内的css、或者内嵌的css,或者当HTML代码遇见标签时,浏览器会发送请求获得该标签中标记的CSS,当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的styleSheets

创建布局树,遍历 DOM 树中的所有可见节点,并把这些节点加到布局中;而不可见的节点会被布局树忽略掉,如 head 标签下面的全部内容,再比如 body.p.span 这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树。最后计算 DOM 元素的布局信息,使其都保存在布局树中。布局完成过程中,如果有js操作或者其他操作,对元素的颜色,背景等作出改变就会引起重绘,如果有对元素的大小、定位等有改变则会引起回流。

因为页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树。

渲染引擎实现图层的绘制,把一个图层的绘制拆分成很多小的绘制指令然后再把这些指令按照顺序组成一个待绘制列表,当图层的绘制列表准备好之后,主线程会把该绘制列表提交给合成线程,合成线程会将图层划分为图块,然后按照视口附近的图块来优先生成位图(实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图)

一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令,然后将该命令提交给浏览器进程,浏览器最后进行显示。

2.如何理解回流和重绘?

回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。

重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。这个过程叫做重绘。 由此我们可以看出,重绘不一定导致回流,回流一定会导致重绘。

常见的会导致回流的元素:
  • 常见的几何属性有 width、height、padding、margin、left、top、border 等等。
  • 最容易被忽略的操作:获取一些需要通过即时计算得到的属性,当你要用到像这样的属性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight 时,浏览器为了获取这些值,也会进行回流。
  • 当我们调用了 getComputedStyle 方法,或者 IE 里的 currentStyle 时,也会触发回流。原理是一样的,都为求一个“即时性”和“准确性”。
避免方式:
  1. 避免逐条改变样式,使用类名去合并样式
  2. 将 DOM “离线”,使用DocumentFragment
  3. 提升为合成层,如使用will-change
#divId {
  will-change: transform;
}
复制代码

优点

  • 合成层的位图,会交由 GPU 合成,比 CPU 处理要快
  • 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层
  • 对于 transform 和 opacity 效果,不会触发 layout 和 paint

注意:

部分浏览器缓存了一个 flush 队列,把我们触发的回流与重绘任务都塞进去,待到队列里的任务多起来、或者达到了一定的时间间隔,或者“不得已”的时候,再将这些任务一口气出队。但是当我们访问一些即使属性时,浏览器会为了获得此时此刻的、最准确的属性值,而提前将 flush 队列的任务出队

四、 JavaScript 中的数据是如何存储在内存中的?

在 JavaScript 中,原始类型的赋值会完整复制变量值,而引用类型的赋值是复制引用地址。

在 JavaScript 的执行过程中, 主要有三种类型内存空间,分别是代码空间、栈空间、堆空间。 其中的代码空间主要是存储可执行代码的,原始类型(Number、String、Null、Undefined、Boolean、Symbol、BigInt)的数据值都是直接保存在“栈”中的,引用类型(Object)的值是存放在“堆”中的。因此在栈空间中(执行上下文),原始类型存储的是变量的值,而引用类型存储的是其在”堆空间”中的地址,当 JavaScript 需要访问该数据的时候,是通过栈中的引用地址来访问的,相当于多了一道转手流程。

在编译过程中,如果 JavaScript 引擎判断到一个闭包,也会在堆空间创建换一个“closure(fn)”的对象(这是一个内部对象,JavaScript 是无法访问的),用来保存闭包中的变量。所以闭包中的变量是存储在“堆空间”中的。

JavaScript 引擎需要用栈来维护程序执行期间上下文的状态,如果栈空间大了话,所有的数据都存放在栈空间里面,那么会影响到上下文切换的效率,进而又影响到整个程序的执行效率。通常情况下,栈空间都不会设置太大,主要用来存放一些原始类型的小数据。而引用类型的数据占用的空间都比较大,所以这一类数据会被存放到堆中,堆空间很大,能存放很多大的数据,不过缺点是分配内存和回收内存都会占用一定的时间。因此需要“栈”和“堆”两种空间。

原文参考

浏览器相关原理(面试题)详细总结一 - 掘金 (juejin.cn)
浏览器相关原理(面试题)详细总结二 - 掘金 (juejin.cn)

阅读全文

cookie、localStorage、sessionStorage

前端随笔 2022/12/14

一、三者的异同

特性 Cookie localStorage sessionStorage
数据的生命期 一般由服务器生成,可设置失效时间。如果在浏览器端生成Cookie,默认是关闭浏览器后失效 除非被清除,否则永久保存 仅在当前会话下有效,关闭页面或浏览器后被清除
存放数据大小 4K左右 一般为5MB 一般为5MB
与服务器端通信 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题 仅在客户端(即浏览器)中保存,不参与和服务器的通信 仅在客户端(即浏览器)中保存,不参与和服务器的通信
易用性 需要程序员自己封装,源生的Cookie接口不友好 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持

二、localStorage和sessionStorage操作

setItem存储value

用途:将value存储到key字段

sessionStorage.setItem("key", "value");     localStorage.setItem("site", "js8.in");

getItem获取value

用途:获取指定key本地存储的值

var value = sessionStorage.getItem("key");     var site = localStorage.getItem("site");

removeItem删除key

用途:删除指定key本地存储的值

sessionStorage.removeItem("key");     localStorage.removeItem("site");

clear清除所有的key/value

用途:清除所有的key/value

sessionStorage.clear();     localStorage.clear();
阅读全文

css滚动条,让滚动条看起来更好看

前端随笔 2022/12/3

css滚动条

相关样式属性说明(详情查看图片,序号一一对应)

  1. ::-webkit-scrollbar 滚动条整体部分,可以设置 width, height, background, border 等
  2. ::-webkit-scrollbar-button 滚动条两端的按钮,可以设置 display:none 让其不显示,也可以添加背景图片、颜色改变其显示效果
  3. ::-webkit-scrollbar-track 外层轨道,可以设置 display:none 让其不显示,也可以添加背景图片、颜色改变其显示效果
  4. ::-webkit-scrollbar-track-piece 内层滚动槽
  5. ::-webkit-scrollbar-thumb 滚动的滑块,也就是可以用鼠标点击拖动的部分
  6. ::-webkit-scrollbar-corner 边角
  7. ::-webkit-resizer 定义右下角拖动块的样式。

159ada1eddb44a00c5f7003f9df9961f.png

/* 改变element表格的滚动条样式 */
.el-table__body-wrapper::-webkit-scrollbar {
    width: 4px; /* 纵向滚动条的大小 */
    height: 10px; /* 横向滚动条的大小 */
}
.el-table__body-wrapper::-webkit-scrollbar-thumb {
    border-radius: 5px;
    -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
    background: rgba(0, 0, 0, 0.2);
}
.el-table__body-wrapper::-webkit-scrollbar-track {
    -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
    border-radius: 0;
    background: rgba(0, 0, 0, 0.1);
}
阅读全文

React基础

recat 2022/11/23

React JSX

全称:javascript XML

作用:用来创建react虚拟dom对象

优点:

  • JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
  • 它是类型安全的,在编译过程中就能发现错误。
  • 使用 JSX 编写模板更加简单快速。

基本语法规则:

  • 遇到以<开头的代码 以标签语法来解析 html的同名标签就转化成html的标签
  • 遇到以{开头的代码 以js语法来解析 标签中的js语法一定要用{}包裹起来

基本使用:

const element = <h1 className="foo">Hello, world</h1>;
ReactDOM.render(element,document.getElementById('root'));

1.使用JSX

ReactDOM.render(
          <div>
          <h1>zy</h1>
        <p data-myattribute = "somevalue">React学习</p>
        </div>
          ,
          document.getElementById('root')
      );

1.1 JSX中使用js语法

  • JSX 中使用 JavaScript 表达式。表达式需写在花括号  {}  中
  • 在 JSX 中不能使用 if else 语句,但可以使用 conditional (三元运算)  表达式来替代。以下实例中如果变量 i 等于 1 浏览器将输出 true, 如果修改 i 的值,则会输出 false.

实例:

ReactDOM.render( 
     <div> 
        <h1>{1+1}</h1>
        <h1>{i == 1 ? 'True!' : 'False'}</h1> 
     </div> 
     , document.getElementById('root') 
);

1.2 JSX中使用样式

React 推荐使用内联样式。我们可以使用 camelCase 语法来设置内联样式. React 会在指定元素数字后自动添加 px 。

var myStyle = { fontSize: 100, color: '#FF0000' }; 
ReactDOM.render( 
        <h1 style = {myStyle}>zy</h1>, 
        document.getElementById('root') 
);

1.3 JSX中使用注释

注释需要写在花括号中

ReactDOM.render( 
        <div>
        <h1>zy</h1>, 
        {/*注释 我无敌,你随意*/}
        </div>
        document.getElementById('root') 
);

1.3 JSX中使用数组

JSX 允许在模板中插入数组,数组会自动展开所有成员:

var arr = [ 
    <h1>666</h1>, 
    <h2>777</h2>, 
]; 
ReactDOM.render( 
    <div>{arr}</div>, 
    document.getElementById('root') 
);
var arr = ['React','html','css']; 
ReactDOM.render( 
    <ul>{arr.map((item,index) =>{
        return <li key={index}>{item}</li>
    )}</ul>, 
    document.getElementById('root') 
);
阅读全文

步骤条小组件的封装

组件 2022/11/15

前言

公司有一个业务流程是需要步骤条的,为该业务新搭建了个h5的项目(v3+vite3.2+vant),h5页面直接嵌套在app端的,导航条是由app端控制的,第一个步骤是由app端完成,后面两步是由我们完成,步骤条等一切样式要求高度的统一,vant等ui库自带的步骤条组件,还需要自己一个个样式穿透的去改样式,得到的可能还不是自己想要的,于是就自己封装了一个,以便自己以后遇到同样的流程或业务,能够直接C+V。不多bbbb,直接上效果图和代码

一、vue3步骤条组件的封装

1.效果图

1668496872696.png

2.代码

其中$px2rem(‘72px’)是因为使用了移动端适配的插件postcss-pxtorem并做了相应的配置后,发现行内样式移动端适配的插件不生效。于是在main.ts里定义了一个全局行内的方法,让行内样式也能移动端适配

// 行内样式移动端适配请加这个$px2rem() 
const px2rem = (px:any) => {
    if(/%/ig.test(px)){ // 有百分号%,特殊处理,表述pc是一个有百分号的数,比如:90%
      return px
    }else{
      return (parseFloat(px) / 37.5) + 'rem'
    }
  }
app.config.globalProperties.$px2rem = px2rem // 放到全局
<template>
      <!-- Steps 步骤条-组件 -->
  <div class="formStep">
    <div v-for="(item,index) in stepData" v-bind:key="index" class="stepContent">
      <div
        :class="((stepActive > index ?'stepItemSuccess stepItem':'stepItem'))"
      >
          <div class="stepShow">
            <!-- 中间样式 -->
            <div :class="index == stepActive-1 ? 'currentStepIndex' :'stepIndex'">
              <span>{{index+1}}</span>
            </div>
            <!-- 步骤文字 -->
            <div class="stepStatus ">
              <div class="stepName">{{item.status}}</div>
            </div>
          </div>
      </div>
      <!-- 线 -->
      <div :class="stepActive-1 <= index? 'stepActiveLine': 'stepLine'" :style="{width:index == stepData.length-1 ? 0+'px' : $px2rem('72px'),border:index == stepData.length-1 ? 'none' : '1px dashed #FFFFFF'}">
          </div>
    </div>
  </div>
  <div class="bgCard">
    <img src="@/assets/images/bgCard.png" :style="{width: $px2rem('265px'),height:$px2rem('40px')}"/>
  </div>
</template>

<script setup lang="ts">
 interface propsStep {
    stepData: any,
    stepActive: number
 }
 const props = withDefaults(defineProps<propsStep>(), {
    stepData: [],
    stepActive: 1
});
</script>

<style scoped lang="scss">
.formStep {
  width: 100%;
  background: linear-gradient(to right, #FE7E41, #FF5D0F, #FF5C0E);
  // background-color: #FF5C0E;
  display: flex;
  justify-content: space-between;
  padding-top: 30px;
  padding-bottom: 24px;
  .stepContent {
    z-index: 999;
  }
  .stepLine {
    height: 2px;
    width: 100%;
    background: #FFFFFF;
    position: absolute;
    top: 12px;
    margin-left: 80px;
    margin-top: 30px;
  }

  .stepActiveLine {
    border: 1px dashed #FFFFFF;
    position: absolute;
    top: 12px;
    margin-left: 80px;
    margin-top: 30px;
  }

  .stepItem {
    display: flex;
    flex-direction:column;

    .stepStatus {
      display: flex;
      justify-content: center;
      align-items: center;
      margin-top: 16px;
    }
    .stepName {
      height: 17px;
      font-size: 12px;
      font-family: PingFangSC-Medium, PingFang SC;
      font-weight: 500;
      color: #FFFFFF;
      line-height: 17px;
      opacity: 50%;
    }

    .stepIndex {
      width: 24px;
      height: 24px;
      text-align: center;
      font-size: 16px;
      font-weight: bold;
      color: #FE7E41;
      background: #FFFFFF;
      border-radius: 50%;
      opacity: 50%;
      display: flex;
      justify-content: center;
      align-items: center;
      margin-left: 32px;
      margin-right: 32px;
    }

    .currentStepIndex {
      width: 24px;
      height: 24px;
      text-align: center;
      font-size: 16px;
      font-weight: bold;
      color: #FE7E41;
      background: #FFFFFF;
      border-radius: 50%;
      border: 3px  #e8e8e8;
      display: flex;
      justify-content: center;
      align-items: center;
      box-shadow: 0 0 0 2px rgb(255 255 255 / 50%);
      margin-left: 32px;
      margin-right: 32px;
    }
  }
}

.bgCard {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: -50px;
}

.formStep .stepItemSuccess.stepItem .stepName {
  opacity: 100%;
}

.formStep .stepItemSuccess.stepItem .stepIndex {
  color: #FE7E41;
  opacity: 100%;
}

.formStep .stepItemActive.stepItem .stepName {
  opacity: 50%;
}

.formStep .stepItemActive.stepItem .stepIndex {
  color: #FE7E41;
  opacity: 50%;
}

</style>

3.使用

在需要这个用到的组件那块导入

import stepBar from ‘@/components/stepBar/stepBar.vue’

//步骤条
<step-bar :stepData="stepData" :stepActive="stepActive"/>

<script lang="ts" setup>
const stepActive = ref(2)
const stepData = ref([
        {
          status: "666"
        },
        {
          status: "222"
        },
        {
          status: "555"
        }
])
<script/>

Props 参数说明

参数名 类型 默认值 说明
stepData Array [] 步骤内容(如上面的‘666’‘222’什么的)
stepActive Number 0 代表进行到了第几步
阅读全文

JavaScript 异步队列实现

前端随笔 2022/11/10

前言

​ 了解JavaScript 异步队列实现,我们需要先了解什么是同步任务,什么是异步任务,异步代码中才区分宏任务微任务,什么是宏任务、微任务?宏任务、微任务有哪些?又是怎么执行的?

一、异步任务和同步任务

同步任务与异步任务

​ js 是一种单线程语言,简单的说就是:只有一条通道,那么在任务多的情况下,就会出现拥挤的情况,这种情况下就产生了 ‘多线程’ ,但是这种“多线程”是通过单线程模仿的,一切javascript多线程都是纸老虎!那么就产生了同步任务和异步任务。

  • 同步和异步任务分别进入不同的执行”场所”,同步的进入主线程,异步的进入Event Table并注册函数。

  • 当指定的事情完成时,Event Table会将这个函数移入Event Queue。

  • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。

  • 上述过程会不断重复,也就是常说的Event Loop(事件循环)。

    那怎么知道主线程执行栈为空啊?js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

let data = [];
$.ajax({
    url:www.javascript.com,
    data:data,
    success:() => {
        console.log('发送成功!');
    }
})
console.log('代码执行结束');
  • ajax进入Event Table,注册回调函数success。
  • 执行console.log('代码执行结束')。
  • ajax事件完成,回调函数success进入Event Queue。
  • 主线程从Event Queue读取回调函数success并执行。

二、宏任务和微任务是什么呢?

宏任务(macrotask) 微任务(microtask)
谁发起的 宿主(Node、浏览器) JS引擎
具体事件 1. script (可以理解为外层同步代码)2. setTimeout/setInterval3. UI rendering/UI事件4. postMessage,MessageChannel5. setImmediate,I/O(Node.js) 1. Promise2. MutaionObserver3. Object.observe(已废弃;Proxy 对象替代)4. process.nextTick(Node.js)
谁先执行 后运行 先运行
会触发新一轮Tick吗 会 不会

1.宏任务、微任务是怎么执行的?

​ 执行顺序:先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。

宏任务、微任务是怎么执行

setTimeout(()=>{
  new Promise(resolve =>{
      resolve();
  }).then(()=>{
      console.log('test');
  });

  console.log(4);
});

new Promise(resolve => {
  resolve();
  console.log(1)
}).then( () => {
  console.log(3);
  Promise.resolve().then(() => {
    console.log('before timeout');
  }).then(() => {
    Promise.resolve().then(() => {
      console.log('also before timeout')
    })
  })
})
console.log(2);

1.遇到setTimeout,异步宏任务,将() => {console.log(4)}放入宏任务队列中;

2.遇到new Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出1;

3.而Promise.then中注册的回调才是异步执行的,将其放入微任务队列中
4.遇到同步任务console.log(2),输出2;主线程中同步任务执行完
5.从微任务队列中取出任务到主线程中,输出3,此微任务中又有微任务,Promise.resolve().then(微任务a).then(微任务b),将其依次放入微任务队列中;
6.从微任务队列中取出任务a到主线程中,输出 before timeout;
7.从微任务队列中取出任务b到主线程中,任务b又注册了一个微任务c,放入微任务队列中;
8.从微任务队列中取出任务c到主线程中,输出 also before timeout;微任务队列为空
9.从宏任务队列中取出任务到主线程,此任务中注册了一个微任务d,将其放入微任务队列中,接下来遇到输出4,宏任务队列为空
10.从微任务队列中取出任务d到主线程 ,输出test,微任务队列为空

三、关系总结

总结

上图也是盗的,自己对这些做个笔记,加深影响,到时候回顾起来也比较方便!

参考文章

​ 这一次,彻底弄懂 JavaScript 执行机制 - 掘金 (juejin.cn)

​ 微任务/宏任务和同步/异步之间的关系_

阅读全文
avatar
耀耀切克闹

欢迎来到耀耀切克闹的博客

不是因为是主角才成功,而是成功了才成为主角

想要的都拥有,得不到的都释怀