醉月思 发布的文章

2020-06-23T12:06:46.png

tips: 请注意区分vue3.0和vue cli 3.x/4.x

创建项目

vue create -r https://registry.npm.taobao.org vue-next-test

2020-06-23T12:01:58.png

选择你喜欢的配置即可,创建成功后,从Demo来看,跟vue2.0的typescript写法差别看起来不大的样子。。。

未完待续

typescript

本文环境

  1. @vue/cli 4.3.1
  2. typescript 3.9.5

相关配置文件

  1. vue.config.js
  2. tsconfig.json
  3. shims-vue.d.ts

问题汇总

导入 vue 文件报错

错误信息:Cannot find module './App.vue' or its corresponding type declarations
解决方案:修改shims-vue.d.ts

declare module "*.vue" {
  import Vue from "vue";
  export default Vue;
}

Vscode 报错,编译不报错

解决方案:重启 Vscode

挂载原型$api 报错

解决方案:在src目录下新增vue-property.d.ts

import Vue from 'vue'
declare module "vue/types/vue" {
  interface Vue {
    $api: any;
  }
}

无法使用@components别名 alias 路径

解决方案: 修改tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": ["webpack-env", "vuex"],
    "paths": {
      "@/*": ["src/*"],
      "@/components": ["src/components"] // 添加这一行
    },
    "lib": ["esnext", "dom", "dom.iterable", "scripthost"]
  },
  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "tests/**/*.ts", "tests/**/*.tsx"],
  "exclude": ["node_modules"]
}

该文章为异教徒解决方案,各位看官看下即可,切勿模仿!!!

问题描述

原生的el-upload只支持上传图片时候的预览和回显,这是因为只针对img标签做了适配,如下图。

2020-06-03T05:42:34.png

而如果我们上传视频,则会出现一个白方框,用户体验不佳。

2020-06-03T05:45:07.png

解决思路

其实正确的解决思路应该是

  1. show-file-list属性设置为false。然后再自己循环显示file-list,以及追加对应的预览,删除按钮及功能。
  2. 设置自定义模板内容(推荐使用)

但是我觉得这样太麻烦了(时间问题)。

于是我突发奇想,如果我将img标签改成video标签呢?如下图

2020-06-03T05:46:56.png

发现居然完美契合,毫无违和感。

预览的实现

在做到把img改为video标签之前,还需要解决的另一个问题就是,如何让视频也支持预览。老规矩,我们先来看看官方Demo怎么实现预览的。

2020-06-03T05:54:03.png

官方的做法是增加一个dialog,然后在点击预览图片时将文件url传给dialog。我们先来实现一下改写一下dialog

<el-dialog :visible.sync="dialogVisible" :modal-append-to-body="true">
      <video width="100%" muted autoplay="autoplay" loop="loop" v-if="dialogImageUrl[dialogImageUrl.length - 1] == 4" :src="dialogImageUrl"></video>
      <img width="100%" v-else :src="dialogImageUrl" alt />
    </el-dialog>
tips: 我这种判断MP4格式的方式实属异端,不建议模仿。

替换img标签

一到标签节点的操作,我第一想到的就是document操作(异端+1),直接上代码。

 changeVideoTag(){
      let videoTag = document.querySelector('.video img')
      console.log('检测到应为video的img标签', videoTag)
      if(videoTag){
        let parentNode = videoTag.parentNode
        let newElement = document.createElement('video')
        newElement.setAttribute('class', videoTag.getAttribute('class'))
        newElement.setAttribute('src', videoTag.getAttribute('src'))
        parentNode.insertBefore(newElement, videoTag)
      }
    },

该函数负责寻找video类下的img标签,然后在img标签之前,添加一个同样的videos元素节点,此处你可以选择是否移除原img标签。

最终实现效果

gif.gif

最后的话

  1. 这种方法非常不推荐使用,强烈建议使用自定义模板缩略图
  2. 这种方法非常不推荐使用,强烈建议使用自定义模板缩略图
  3. 这种方法非常不推荐使用,强烈建议使用自定义模板缩略图

u=3276836620,1954497454&fm=26&gp=0.jpg

remove-files-webpack-plugin

原理就是在项目中添加一个webpack插件,然后配置插件
项目根目录新增vue.config.js

const path = require('path')
const RemovePlugin = require('remove-files-webpack-plugin')

module.exports = {
    configureWebpack: {
        plugins: [
            new RemovePlugin({
                after: {
                    root: path.join(__dirname, './unpackage'),
                    include: [
                        path.join(__dirname, 'unpackage/dist', process.env.NODE_ENV === 'production' ?
                            'build' : 'dev', process.env
                            .UNI_PLATFORM, './mp-weixin/static/APPPIC')
                    ],
                    trash: false
                }
            })
        ]
    }
}

部分webpack类似的webpack插件

  1. copy-webpack-plugin
  2. clean-webpack-plugin

持续集成

Gitlab的持续集成

我们可以将整个运行机制,看作一个赏金猎人接任务,执行任务,并完成任务的过程。

GitLab-CI

简单来说,这就是一个任务发布平台。运行在gitlab服务器,监听代码状态变化,并发布对应的任务。

GitLab-Runner

而每个runner就是一位赏金猎人,是任务的执行者。

2020-05-22T06:04:52.png

.gitlab-ci.yml

任务的发布者,规定什么时候触发任务,任务的具体内容。

配置流程

经过前面的解释,整个思路就很清晰了。我们需要做的有三件事。

  1. 编写.gitlab-ci.yml文件,设置对应的任务
  2. 部署Runner,激活赏金猎人
  3. 配置ci,邀请赏金猎人加入系统

部署Runner

这一步需要一个服务器,能run起来赏金猎人。

安装

请务必安装最新版,不然会出现很多未知的问题

  1. 下载二进制文件
# Linux x86-64
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64

# Linux x86
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386

# Linux arm
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm

# Linux arm64
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm64
  1. 授予执行权限
sudo chmod +x /usr/local/bin/gitlab-runner
  1. Create a GitLab CI user:
sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
  1. Install and run as service:
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start

加入任务系统

注册

sudo gitlab-runner register

然后就是一些简单的配置,配置完成后就将该Runner注册到任务发布平台了,然后就可以接任务了。详细见参考文献【1】

编写.gitlab-ci.yml任务

本机部署版本
.gitlab-ci.yml

stages:
  - deploy

cache:
  paths:
    - node_modules/
    - public/

deployJob:
  stage: deploy 
  script:
    - npm install 
    - npm run build
    - rm -rf /home/data/three_miju_shopper_manager_system_front/*
    - cp -rf ./dist/* /home/data/three_miju_shopper_manager_system_front/
    - sh ./bot.sh ${CI_COMMIT_REF_SLUG} ${CI_COMMIT_SHA:0:8} ${CI_COMMIT_MESSAGE}
  tags:
    - shared_test_machine_runner
  only:
    - dev

这个版本具有企业微信群机器人推送功能,需要配置./bot.sh

#!/usr/bin/env bash
curl '群机器人地址' \
      -H 'Content-Type: application/json' \
      -d '
      {
        "msgtype": "markdown",
        "markdown": {
          "content": "商户端代码已更新,分支:'$1' 提交:'$2'
          更新:'$3'
          已发布,[点击测试](http://test.shop.gileey.cn)"
        }
      }'

远程推送版本

stages:
  - deploy

cache:
  paths:
    - node_modules/
    - public/

deployJob:
  stage: deploy 
  script:
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" >> ~/.ssh/id_dsa
    - chmod 600 ~/.ssh/id_dsa
    - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
    - rsync -avzu --progress ./dist/* root@thinkmoon.cn:/www/wwwroot/3ju.psyannabel.cn/
  tags:
    - shared_test_machine_runner
  only:
    - dev

该版本在gitlab-runner机器上执行编译等工作,编译完成后使用rsync同步到云服务器,需要配置私钥变量$SSH_PRIVATE_KEY

2020-05-30T14:20:12.png

遇到的问题

导入自定义组件时一直报错:This dependency was not found:

出现背景:由于以前命名组件是"clickImg",后改成"ClickImg",由于linux的区分大小写,所以会一直没找到。

解决方案:换个名字???

参考文献

  1. 前端的gitlab的ci初尝试
  2. Install GitLab Runner manually on GNU/Linux

为什么需要?

微信小程序里面页面与自定义组件的区别较大,而页面的可复用程度太低了。要么就是跳转页面,要么就是copy代码。如果我们要增加代码的可复用性,就可以借用Vue组件的思想,将页面改写为自定义组件。

结构对比

两者的视图文件,样式文件没有什么区别。主要区别最大的就是js文件,我们首先来看一下两者的结构。

page.js

page({
  // 数据内容
  data:{},
  // 生命周期函数
  onLoad(){},
  // 一些自定义函数
  someMethods(){}
})

componet.js

componet({
  // 预设属性
  properties: {},
  // 数据内容
  data: {},
  //组件所在页面生命周期
  pageLifetimes: {},
  // 生命周期函数-挂载
  attached: function() {
    // 在组件实例进入页面节点树时执行
  },
  // 生命周期函数-卸载
  detached: function() {
    // 在组件实例被从页面节点树移除时执行
  },
  // 自定义函数写结构里面
  methods:{
    someMethods(){}
  }
})

我们可以看到,两者的基本相似,大同小异。所以改写起来就特别方便啦~

  1. 将page改为componet
  2. 适配生命周期函数,例如将页面onLoad()函数改写为组件的attached()函数
  3. 将自定义函数someMethods()写到Componet的methods里面

按官网文档一键配置按需引入总是无法生效,目测可能是激进式预设没有选babel的原因。故写此文详细记录折腾过程,望有效!

vue ui创建新项目

选择预设,bable,eslint,vuex,vue-router(历史模式)

vue ui安装element插件

选择配置

2020-05-01T03:05:01.png

改动的内容

  1. 自动创建了bable.config.js,并添加了内容。
  2. 创建了`@/src/plugins/element.js,设置了按需引入
  3. 改写了app.vue,添加了element按钮组件

运行测试

2020-05-01T03:10:21.png

加载成功

2020-05-01T03:11:11.png

Q&A

  1. 为什么需要bable来支持按需加载

如果不使用bable转码,那你按需引入会找不到该组件。这是由于bable在将组件库转码的过程中把element整个组件库编译成了各种独立的组件模块,进而实现按需加载的功能

  1. 如何添加需要的组件

使用vue ui添加后的组件会在plugins目录下创建element.js文件,然后在main.js中引入该文件。文件内容如下:

import Vue from "vue";
import { Button } from "element-ui";

Vue.use(Button);

这就是只引入Button的方法,如果要按需添加,只需要依葫芦画瓢即可!

2020-04-19T02:47:53.png

安装

全局安装@vue/cli

yarn global add @vue/cli

检查是否安装成功(需重启更新环境变量)

vue --version

2020-04-19T02:36:37.png

快速原型开发

新版本Vue中增加了该功能,方便快速进行单个Vue文件开发,需要先额外安装一个全局的扩展。

yarn global add @vue/cli-service-global

入手尝鲜

新建index.vue文件

<template>
    <div>Hello Vue</div>
</template>

运行服务

vue serve index.vue

效果

2020-04-19T02:47:22.png

创建项目

vue.config.js

在vue cli 3.x/4.x 中,使用vue.cofig.js来进行一些包括webpack的配置。比如我们可以想要一个控制台输出编译时间的配置,可以在如下设置

const moment = require('moment')

module.exports = {
  chainWebpack: config => {
    config
      .plugin('html')
      .tap(args => {
        args[0].title = '三只蜜桔后台管理系统·商户版',
        args[0].buildTime = moment().format('YYYY.MM.DD.HH.mm')
        return args
      })
  }
}

亦或者,我们想要在生产环境禁用console.log

const moment = require('moment')

module.exports = {
  chainWebpack: config => {
    config
      .plugin('html')
      .tap(args => {
        args[0].title = '三只蜜桔后台管理系统·商户版',
        args[0].buildTime = moment().format('YYYY.MM.DD.HH.mm')
        return args
      })
    config.optimization
      .minimizer('terser')
      .tap(args => {
        Object.assign(args[0].terserOptions.compress, {
          pure_funcs: ['console.log']
        })
        return args
      })
  },
  productionSourceMap: false,
  configureWebpack: {
    output: {
      filename: `${moment().format('YYYY.MM.DD.HH.mm')}.${process.env.NODE_ENV}.[name].js`,
      chunkFilename: `${moment().format('YYYY.MM.DD.HH.mm')}.${process.env.NODE_ENV}.[name].js`
    }
  }
}

前言

由于各种终端屏幕风格,大小迥异,由此而生衍生出了自适应页面设计。如果我们需要在各种机型上显示的风格比例一致,而解决屏幕大小不同显示内容不同的问题的话,我们需要对各种屏幕比例做适配。本文就该问题分享一种可行性方案。

由于以前做的移动端页面大部分为小程序页面,小程序采用相对像素(rpx)实现响应式适配。故本文也采用类似思想(rem)。

1. rem是什么

在W3C官网上是这样描述rem的——“font size of the root element”

即相对根节点(html)的字体大小,那么解决方案的思路就立马来了。根据不同的屏幕大小,设置不同的html字体大小,这样其他使用rem单位的元素即会随之自适应的改变大小。

2. 计算根节点(html)字体大小

2.1 JS方案

思路就是使用js获取窗口宽度,然后根据宽度计算对应的font-size。根据一般常识,js为了不影响页面体验,应在文档末尾添加。但此处为了避免HTML渲染完成后,使用JS动态修改字体而造成的页面抖动问题。我们应该将该JS元素节点放置于header底部,并内联到html文档里面。例如

在Vue中直接修改template.html即可
<script type="text/javascript">
    (function (doc, win) {
      var docEl = doc.documentElement,
        resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
        recalc = function () {
          var clientWidth = docEl.clientWidth;
          docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
        };
      if (!doc.addEventListener) {
        return;
      }
      win.addEventListener(resizeEvt, recalc, false);
      doc.addEventListener('DOMContentLoaded', recalc, false);
      recalc()
    })(document, window)
</script>

该代码段监听页面尺寸改变事件,根据对应窗口大小(clientWidth)除以一个基准(750),进而调整html的font-size。在本例下,375的屏幕中1rem=50px。

2.2 CSS方案

采用媒体查询,适配不同宽度,代码略。

Vue配置px自动转rem

postcss-plugin-px2rem介绍

A plugin for PostCSS that generates rem units from pixel units.

也就是说,使用该插件可以自动的将你写px单位转为rem单位,而且不用担心污染问题,因为可以设置选择黑名单。

安装postcss-plugin-px2rem

yarn add postcss-plugin-px2rem

配置

待补充

总结

该方法只适用于对于不同屏幕的屏幕显示相同的比例,对于那些内容式网站则要避免使用该方法。此时,用户选择更多屏幕的目的不是为了看到更大的字体,而是希望能够看到更多的内容。

参考文献

简单粗暴的移动端适配方案 - REM

前言

相信小伙伴们对广告是又爱又恨,一方面不想接受广告的荼毒,另一方面也想接点广告赚点流量收益。由此而来衍生出了两个技术,屏蔽广告与防屏蔽广告。

2020-04-14T09:05:00.png
屏蔽广告

2020-04-14T09:07:07.png
防屏蔽

原理

我们要想做到网站广告不被人屏蔽,首先就要理解屏蔽广告的原理。

1.初阶屏蔽

含ad,推广等字样的js或者class类就直接拦截或者不显示

2.高阶屏蔽

我也不会,但是高阶屏蔽肯定是可以屏蔽低阶广告的。

那么根据这个原理,我们可以发现。只要我们写一个很low很low的广告,然后监听它是否成功加载,如果没有,则跳转页面或者不显示内容。

实现

1. 制造广告

先写一个一眼就能看出来是广告的js文件,例如把js文件名命名
为"adview_pic_cpc_cpm_cpa_guanggao_gg_ads_300x250.js"。广告二字赤裸裸的写着,广告的韵味呼之欲出。

2. 定义变量

在js里面定义一个变量,比如:

var adskilltest=true;

3. 监听广告变量

页面文件中读取该变量

不管怎么样,只要被拦截,那就肯定是undefined。让个极其肤浅的广告命名,来检测是否有屏蔽广告,这叫引蛇出洞!

2020-04-14T09:20:46.png

大功告成!

4. 后续操作

既然被拦截,就要做出点反应。有了反应之后呢,还需要保存当时浏览的页面连接。我的解决方法是写一个noads.html负责显示被拦截后的内容以及保存跳转前的链接。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>温馨提示</title>
</head>
<body>
<p>网站内容被识别为广告,已被屏蔽。请禁用广告屏蔽后点击下面链接哦!</p>
<a href="#" id="a">我已关闭广告屏蔽功能</a>
</body>
<script>
    function getQueryVariable(variable)
{
       var query = window.location.search.substring(1);
       var vars = query.split("&");
       for (var i=0;i<vars.length;i++) {
               var pair = vars[i].split("=");
               if(pair[0] == variable){return pair[1];}
       }
       return(false);
}
document.getElementById("a").href = getQueryVariable('route');
</script>
</html>
tips: 该方法不是最优,但是可行!