2020年尾末问题总结

Aditya2021-02-28开发问题

2020年尾末问题总结

  1. CSS的一个bug
  2. vue中markdown解析
  3. CSS-伪元素、定位和z-index的结合
  4. 浏览器 navigator.clipboard is undefined
  5. 微信自定义分享
  6. Js按A-Z排序通讯录)
  7. keep-alive 的使用
  8. 页面可见性API
  9. Vue采用CDN方式引入基础第三方库
  10. vue-cli3项目开启gzip压缩

1. CSS的一个bug

CSS的一个bug,由于语法过时,导致此部分内容不生效

Gradient has outdated direction syntax. New syntax is like to left instead of right.

    background: linear-gradient(left, #FF9999 0%,#FF6F6F 100%);

    background: linear-gradient(225deg, #FF9999 0%, #FF6F6F 100%);

2. vue中markdown解析

2.1 安装依赖

npm install hyperdown prismjs -D

2.2 新增 markdown-loader.js

const HyperDown = require('hyperdown');
const Prism = require('prismjs');

function markdownLoader(val) {
  let parser = new HyperDown();
  let html = parser.makeHtml(val);
  html = html.replace(/(?<=<pre><code[^>]*?>)[\s\S]*?(?=<\/code><\/pre>)/gi, v => {
    v = v.replace(/_&/g, ' ').replace(/&quot;/g, '"').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
    return Prism.highlight(v, Prism.languages.javascript);
  });
  return (
    `<template><div class="markdown">${html}</div></template>`
  );
}

module.exports = markdownLoader;

2.3 使用

// vue-cli 3.x ,在vue.config.js添加以下配置
module.exports = {
  configureWebpack: config => {
    config.module.rules.push({
        test: /\.md$/,
        use: [
          {
            loader: 'vue-loader',
          },
          {
            loader: require.resolve('./markdown-loader'),
          },
        ],
      },
    );
  },
};
// webpack.conf.base.js 里新增loader

在入口文件main.js导入prismjs样式

// 
import 'prismjs/themes/prism.css';

3. CSS-伪元素、定位和z-index的结合

应用场景是元素盒子通过设置z-index来改变伪元素before, after与元素内容的层叠关系

<div class="ele">
    aaaaaaaaaaaa
</div>
<style>
.ele {
        position: relative;
        width: 100px;
        height: 100px;
        background-color: red;
        font-size: 20px;
        z-index: 1; 
    }
    .ele::before {
        content: '';
        background-color: yellow;
        width: 100px;
        height: 100px;
        position: absolute;
        top: 20px;
        left: 20px;
        z-index: -1;
    }
</style>

image

如上图所示,元素层级只要设置z-index,其内伪类为-1也不会到最底层

4. 浏览器 navigator.clipboard is undefined

let clipboard = navigator.clipboard;
if (clipboard == undefined) {
  // alert('clipboard is undefined');
} else {
  navigator.clipboard.readText().then(clipText => {
    this.pasteTxt = clipText
  });
}

该API需要在安全网络下并且需要授权才可以使用,IE不支持

localhost或者https,127.0.0.1是本机域名是安全的,所以可以获取到clipboard,谷歌现在很多api都是必须要求https的情况的

4.1 在微信浏览器调用该API不会直接获取值,而是出现一个提示框包含粘贴和搜索(ios),还需要用户手动点击才可以赋值。

5. 微信自定义分享

记录写的两种方式,一种是跟后台取jsapi_token,一种是跟后台取已经写好的 wx.config 里全部内容

5.1 jsapi_token

import axios from 'axios'
import sha1 from 'hex-sha1'
import wx from 'weixin-js-sdk'
let url = window.location.href.split('#')[0];
let jsapi_ticket = '',
    timestamp=0,
    nonceStr,
    string1,
    signature,
    wxTokenUrl= 'https://snailsleep.net/snail-confession/confession/get/token';
function base_objKeySort(arys) {
    //先用Object内置类的keys方法获取要排序对象的属性名,再利用Array原型上的sort方法对获取的属性名进行排序,newkey是一个数组
    let newkey = Object.keys(arys).sort();
    //console.log('newkey='+newkey);
    let newObj = {}; //创建一个新的对象,用于存放排好序的键值对
    for (let i = 0; i < newkey.length; i++) {
        //遍历newkey数组
        newObj[newkey[i]] = arys[newkey[i]];
        //向新创建的对象中按照排好的顺序依次增加键值对
    }
    return newObj; //返回排好序的新对象
}
function base_getNowTimeStamp() {
    let time = new Date();
    let timeStamp = Date.parse(new Date(time));
    return timeStamp;
}
function base_randomWord(randomFlag, min, max) {
    let str = "",
        pos,
        range = min,
        arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
    // 随机产生
    if (randomFlag) {
        range = Math.round(Math.random() * (max - min)) + min;
    }
    for (let i = 0; i < range; i++) {
        pos = Math.round(Math.random() * (arr.length - 1));
        str += arr[pos];
    }
    return str;
}
const wxShare = function () {
    timestamp = base_getNowTimeStamp();
    timestamp = String(timestamp).slice(0, 10);
    timestamp = Number(timestamp);
    nonceStr = base_randomWord(false, 16);
    let paramsData = {
        noncestr: nonceStr,
        jsapi_ticket: jsapi_ticket,
        timestamp: timestamp,
        url: url,
    }
    let data = base_objKeySort(paramsData);
    // console.log(data);
    string1 = JSON.stringify(data);
    let stringSignTemp = string1.substring(1, string1.length - 1);
    stringSignTemp = stringSignTemp.replace(/"/g, "");
    stringSignTemp = stringSignTemp.replace(/,/g, "&");
    stringSignTemp = stringSignTemp.replace(/:/, "=");
    stringSignTemp = stringSignTemp.replace(/:/, "=");
    stringSignTemp = stringSignTemp.replace(/:/, "=");
    stringSignTemp = stringSignTemp.replace(/:/, "=");
    // console.log(stringSignTemp);
    signature = sha1(stringSignTemp);
    wx.config({
        debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
        appId: 'wxb71e95ab1ea7f54d', // 必填,公众号的唯一标识
        timestamp: timestamp, // 必填,生成签名的时间戳
        nonceStr: nonceStr, // 必填,生成签名的随机串
        signature: signature, // 必填,签名
        jsApiList: [
            'onMenuShareTimeline',
            'onMenuShareAppMessage',
        ], // 必填,需要使用的JS接口列表
    });
    wx.ready(function() {
        // alert('微信成功');
        // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
        wx.onMenuShareAppMessage({
            title: '', // 分享标题
            desc: '', // 分享描述
            link: url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
            imgUrl: '', // 分享图标
            type: '', // 分享类型,music、video或link,不填默认为link
            dataUrl: '', // 如果nzbzpe是music或video,则要提供数据链接,默认为空
            success: function() {
                // 用户点击了分享后执行的回调函数
            }
        });
        wx.onMenuShareTimeline({
            title: '', // 分享标题
            link: url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
            imgUrl: '', // 分享图标
            success: function() {
                // 用户点击了分享后执行的回调函数

            },
        });
    });
    wx.error(function(res) {
        console.log(res)
        // alert('微信失败')
        // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
    });
}
export default  {
    install(Vue) {
        // 获取微信token
        Vue.prototype.getWxtoken=function() {
            axios.get(wxTokenUrl)
                .then(res => {
                    jsapi_ticket = res.data;
                    wxShare();
                })
                .catch(err => {
                    console.log(err);
                });
        }
        Vue.prototype.base_isEquipment = function() {
            const UA = navigator.userAgent,
                isAndroid = /android|adr|linux/gi.test(UA),
                isIOS = /iphone|ipod|ipad/gi.test(UA) && !isAndroid,
                isBlackBerry = /BlackBerry/i.test(UA),
                isWindowPhone = /IEMobile/i.test(UA),
                isMobile = isAndroid || isIOS || isBlackBerry || isWindowPhone;
            return {
                isAndroid: isAndroid,
                isIOS: isIOS,
                isMobile: isMobile,
                isWeixin: /MicroMessenger/gi.test(UA),
                isQQ: /QQ/gi.test(UA),
                isPC: !isMobile,
                isWeibo: /WeiBo/gi.test(UA)
            }
        }
    }
}

5.2 wx.config

axios({
        url: `/api/weChat/jsApiSdkAuth`,
        method: 'POST',
        params: {
          link: decodeURIComponent(location.href)
        }
      })
          .then(({data}) => {
            if (data.code === '1') {
              wx.config({
                debug: true,
                appId: data.data.appId,
                timestamp: data.data.timestamp,
                nonceStr: data.data.nonceStr,
                signature: data.data.signature,
                jsApiList: [
                  'onMenuShareAppMessage', 'onMenuShareTimeline', 'updateAppMessageShareData', 'updateTimelineShareData'
                ],
              })
              wx.ready(() => {
                let shareData = this.shareData
                //兼容新老版本接口, 如不需要处理逻辑情况下, 调试好可以直接使用
                if (wx.onMenuShareAppMessage) { //微信文档中提到这两个接口即将弃用,故判断
                  wx.onMenuShareAppMessage(shareData);//1.0 分享到朋友
                  wx.onMenuShareTimeline(shareData);//1.0分享到朋友圈
                } else {
                  wx.updateAppMessageShareData(shareData);//1.4 分享到朋友
                  wx.updateTimelineShareData(shareData);//1.4分享到朋友圈
                }
                wx.error(function(res) {
                  console.log(res)
                });
              });
            } else {
              Toast(data.msg)
            }
          })

最本质的还是根据微信的坑爹文档来灵活应变。

坑1:我一直以为微信自定义分享也可以通过按钮来调起原生的分享……
坑2:自定义分享的link跟当前页面的链接不一致,签名的时候还需要签 location.herf,这个问题找了好久

大多数遇到情况下在 微信分享填坑指南 都能找到解决方案。

微信分享填坑指南open in new window

6. Js按A-Z排序通讯录

直接抄的轮子,作者说有Bug,也没时间去细细看,能满足需求

Js按A-Z排序通讯录(中文、英文、特殊字符open in new window

7. keep-alive 的使用

主要是用来实现类似 前进刷新后退不刷新 的功能,但写出来多多少少还是挺麻烦的,实际需求未能满足废除了,以后根据需求再次开发吧

8. 页面可见性API

配合粘贴板使用

document.addEventListener('visibilitychange', function () {
      // 用户离开了当前页面
      if (document.visibilityState === 'hidden') {
        document.title = '页面不可见';
      }
      // 用户打开或回到页面
      if (document.visibilityState === 'visible') {
        document.title = '页面可见';
      }
    })

9. Vue采用CDN方式引入基础第三方库

Staticfile CDNopen in new window 搜索开源库的cdn链接

9.1 public/index.html

<!DOCTYPE html>
<html lang="en">
.....
    <body>
        <!-- 引入组件库 -->
        <script src="https://cdn.staticfile.org/vue/2.6.11/vue.min.js"></script>
        <script src="https://cdn.staticfile.org/vuetify/2.2.20/vuetify.min.js"></script>
        <script src="https://cdn.staticfile.org/vuex/3.1.3/vuex.min.js"></script>
        <script src="https://cdn.staticfile.org/vue-router/3.1.3/vue-router.min.js"></script>
        <script src="https://cdn.staticfile.org/axios/0.19.2/axios.min.js"></script>
    </body>
</html>

9.2 main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import axios from 'axios'
import VueAxios from 'vue-axios'
import VueRouter from 'vue-router'

9.3 vue.config.js

module.exports = {
	..., // 其他配置省略,主要是加一项externals
    externals: {
      'vue': 'Vue',
      'vue-router': 'VueRouter',
      'vuex': 'Vuex',
      'axios': 'axios'
    }
}

10. vue-cli3项目开启gzip压缩

const CompressionPlugin = require("compression-webpack-plugin");
moudle.export = {
    configureWebpack: () => {
        if (process.env.NODE_ENV === 'production') {
            return {
                plugins: [
                    new CompressionPlugin({
                        test: /\.js$|\.html$|\.css$|\.jpg$|\.jpeg$|\.png/,
                        threshold: 10240,
                        deleteOriginalAssets: false
                    })
                ]
            }
        }
    }
}

10.1 Cannot read property 'tapPromise' of undefined

解决的方案是降级处理

yarn add compression-webpack-plugin@5.0.1
Last Updated 2024/12/27 11:36:49