醉月思 发布的文章

2020-11-30T13:14:59.png

引言

开发过微信小程序的同学想必都对wx.showModal不陌生。用起来还是比较方便的,以api的形式挂载在全局对象wx上,只需调用一下这个api即可显示一个弹窗,还可以根据设置的参数做一些定制。一些知名的组件库,也实现了此类功能。比如element的$message。所以,我也来分享一个简单实现方式,以此来加深对Vue的理解。

代码结构

2020-11-30T15:17:32.png

在Message目录下,有着两个文件,vue文件负责组件内容,js负责处理服务APi。

实现原理

首先使用vue文件创建一个弹窗组件,然后按正常写组件方式写一个组件逻辑。唯一不同的是引入方式不一样,不在别的组件中引用该组件。使用js文件将其注册到全局api,调用该api。

实现过程

注意:本文使用的是Vue3.x版本。由于本人Vue3.x也正出于学习之处,如有理解/实现不当之处,恳请指正!

Vue文件

<template>
    <div v-if="visible" class="tm-message-wrapper">
        <div class="tm-message-dialog">
            <div class="tm-message-title">{{ title }}</div>
            <div class="tm-message-content">{{ content }}</div>
            <div class="tm-message-noRepeat" v-if="showNoRepeat"><input type="checkbox" v-model="noRepeat">不再提示</div>
            <div class="tm-message-operation-area">
                <div class="tm-message-cancel tm-message-operation" @click="action('cancel')">取消</div>
                <div class="tm-message-confirm tm-message-operation" @click="action('confirm')">确定</div>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    data() {
        return {
            uid: "default",
            visible: false,
            title: "提示",
            content: "消息内容",
            showNoRepeat: false,
            noRepeat: false,
            callBack: null
        }
    },
    methods: {
        action(action) {
            this.visible = false
            if (action == "confirm" && this.showNoRepeat && this.noRepeat) {
                window.localStorage.setItem(this.uid, true)
            }
            if (this.callBack instanceof Function) {
                this.callBack(action)
            }
        }
    }
}
</script>

该文件没有什么特别值得注意的地方,唯一一个跟普通组件写法不一样的就是多了一个callBack回调。

js文件

import { createApp } from 'vue'
import Message from "./index.vue"


const msg = options => {
    const NoticeInstance = createApp(Message)
    let msgNode = document.createElement('div');
    NoticeInstance.vm = NoticeInstance.mount(msgNode);
    if (options && Object.keys(options).length > 0) {
        Object.assign(NoticeInstance.vm, options)
    }
    console.log(NoticeInstance)
    NoticeInstance.vm.visible = true;
    NoticeInstance.dom = NoticeInstance.vm.$el;
    document.body.appendChild(NoticeInstance.dom)
    return NoticeInstance.vm
}
export default msg;

js文件首先创建了一个app实例,然后将其挂载到一个新的html元素,随后进行一些属性的赋值操作。最后将实例追加之文档的body末尾。

引入

main.js

import { createApp } from 'vue'
import App from './App.vue'

import Message from "../tm-ui/components/Message"

const app = createApp(App)
app.config.globalProperties.$message = Message;

app.mount('#app')

调用

test.vue

<template>
  <div @click="showModal" class="bg-blue text-lg">
      点我弹窗(wx.showModal)
  </div>
</template>

<script>
export default {
    methods:{
        showModal(){
            this.$message({
                title:"弹窗标题",
                content:"弹窗内容",
                callBack: (action) => {
                    if(action == 'confirm') {
                        console.log("点击了确定")
                    }
                }
            })
        }
    }
}
</script>

<style>

</style>

2020-11-30T15:18:35.png

后记

相比于Vue2.x,vue3.x的设计理念更偏向于函数式编程,比如不在默认导出Vue对象,而是使用createApp的方式创建一个实例。而这个showModal的实现虽然达到了效果,但用起来依旧缺少了函数式编程的美感。相比于微信小程序showModal的对象函数回调写法。我个人更偏向于element的messag实现。返回一个Promise对象,确认操作在then()调用里面,取消操作在catch()异常处理。感觉这样会更具有与Vue3统一的编程式美感。

前言:最近在网络上发现了几位跟我使用同一头像的朋友,更加刺激了我想要制作一个专属头像的想法。然后我就想到了使用一些业界知名开源软件来制作一个属于我的专属头像的想法。

介绍

一款视频换脸的开源软件

deepfacelab下载

源代码地址deepfacelab,可在项目的readme页面下载window环境的执行文件,最让人舒服的是,它帮我们解决了环境问题。我唯一需要解决的环境问题,就是安装显卡驱动(如果有显卡的话)

安装

安装文件是个exe文件,双击运行会进行解压。解压后文件大致如下,根目录是一些批处理命令。workspace是工作区,放一些源文件和目标文件视频,而_internal则存放着一些环境相关文件,如Vs code,cuda, python等,所谓贴心至极,莫过于此。

2020-10-25T12:32:22.png

使用

如它贴心的环境依赖一般的容易使用,你甚至只需要点击几个文件文件,再敲几个回车,就可以完成AI换脸的全过程。大致可分为五个过程

  1. 视频抽帧
  2. 人脸提取
  3. 训练模型
  4. 合成模型
  5. 生成视频

效果展示

2020-10-25T12:40:19.png

贴心小技巧

  1. 一定要记得把aligned里面的质量差的人脸在训练前删除
  2. 由于dst是全帧抽取,而视频合成则是以60帧每秒合成,如果原dst没有60帧,则会出现音画不同步的情况
  3. 换脸后适当提高图片锐度,会极高的提高换脸清晰度
  4. 建议将mask_blur_modifier调至150左右以提升换脸真实性

weblog.png

WeBlog

996.icu

typecho博客的小程序版,支持微信小程序,QQ小程序

特性

音频播放

支持Aplayer插件播放audio/音乐

2020-02-21T03:36:30.png

跨平台编译

基于uni-app开发,支持多端编译。包括但不限于,APP,微信小程序,快应用,QQ小程序,支付宝小程序,H5移动端

微信小程序QQ小程序
微信小程序二维码QQ小程序二维码

一键换肤

采用互补色彩设计原则,支持一键修改主题色。修改文件中的RGB值,即可一键换肤。下面提供一些颜色供以参考。

知乎蓝

首页文章页关于页
2020-02-21T02:35:52.png2020-02-21T02:36:10.png2020-02-21T02:36:31.png

思否绿

首页文章页关于页
2020-02-21T02:42:26.png2020-02-21T02:42:41.png2020-02-21T02:42:52.png

CSDN红

首页文章页关于页
2020-02-21T02:44:27.png2020-02-21T02:44:41.png2020-02-21T02:44:53.png

掘金蓝

首页文章页关于页
2020-02-21T02:46:51.png2020-02-21T02:47:06.png2020-02-21T02:47:16.png

简书红

首页文章页关于页
2020-02-21T02:48:58.png2020-02-21T02:49:07.png2020-02-21T02:49:17.png

GitHub灰

首页文章页关于页
2020-02-21T02:50:57.png2020-02-21T02:51:07.png2020-02-21T02:51:16.png

开发框架

本项目采用uni-app+colorUI开发

快速开始

小程序端

自行编译

适用于有前端基础的同学

  1. 下载Hbuilderx
  2. 将文件夹 uni-app 导入项目
  3. 发行至微信小程序

不编译直接使用

适用于零基础或基础较差的同学

  1. 打开uni-app/unpackage/dist/build/mp-weixin目录导入小程序开发者工具
  2. 激活插件后,修改请求链接文件 @/static/utils/api.js 中的baseUrl, 将域名更换为你的域名。 请修改文件@/common/vendor.js,搜索www.thinkmoon.cn替换为你的域名。

Typecho插件端

复制 WeBlog 到插件目录, 在后台激活并设置

特别鸣谢

虽然项目的作者显示的只有我一个,但是我一个人是无法完成该项目的,我想感谢与该项目相关的一些开源项目及作者。

感谢各位开源作者优秀的作品!

意见反馈

我会尽最大的努力确保文档和代码没有错误。可是,金无赤足,错误在所难免。如果您发现本项目中的任何错误,如错别字或代码错误等,希望您能及时给我反馈,您的反馈不仅可以让其他使用者收益,更可以提高项目的质量。

如果您对于项目有些好的建议,或者想法,欢迎您加入QQ群与我讨论。

QQ群

如果您想与本项目作者或者其他使用者沟通,欢迎加入项目开发交流群:1062676924

开发计划

  • [x] 浏览量和点赞量数据表
  • [x] 评论功能
  • [x] 点击图片预览功能
  • [x] 点击链接复制功能
  • [x] Aplayer插件的支持
  • [x] 一键修改主题色
  • [x] 文章搜索
  • [x] 接口加密
  • [ ] 评论回复功能
  • [ ] 评论通知功能
  • [ ] 网页端微信登录
  • [ ] 夜间模式

更新日志

v0.2.0
feature

  1. 采用完全的自定义组件模式,适配一键换肤
  2. 优化一键换肤的功能。

v0.1.9
feature

  1. 采用互补色彩设计原则,支持一键修改主题色

fixed

  1. 修复文章分类不同步的BUG
  2. 修复在部分情况下缩略图不显示的BUG
  3. 修复遇到

- 阅读剩余部分 -

笔者初学前端时,遇到一些具有padding/border的子元素铺满父元素时,常常采用人工计算width的方式来实现效果。知道后来了解到box-sizing,才知道那种实现方式是多么的尴尬!

box-sizing

box-sizing 属性允许你以某种方式定义某些元素,以适应指定区域。

box-sizing具有三个值

  1. content-box,默认值,指定盒模型为 W3C 标准模型,设置 border、padding 会增加元素 width与 height 的尺寸。
  2. border-box,指定盒模型为 IE模型(怪异模式),设置 border、padding 不会影响元素 width 与 height 的尺寸。
  3. inherit,继承

举个例子

<template>
<block>
  <div class="parent"></div>
 <div class="child"></div>
</block>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String
  }
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
div{
  display: inline-block;
  height: 100px;
  width: 100px;
  padding: 10px;
  border-width: 10px;
  margin: 10px;
}
.parent {
  box-sizing: content-box;
}
 .child {
    box-sizing: border-box;
  }
</style>

效果展示

parent

2020-09-20T14:40:14.png

上图我们可以看到,content-box下,width的宽度只是content的宽度,元素的实际宽度等于width+border+padding

child

2020-09-20T14:42:40.png

而在border-box,width的宽度指的是边框盒子的宽度,元素的实际宽度就等于width

介绍

Vue中的指令形如v-*,如v-if,v-show,v-model等。同时,除了Vue自带的一些默认指令外,Vue同时运行用户自定义指令,来扩展指令功能。

使用场景

究竟什么情况下使用组件?而什么情况下使用指令呢?官方对此,给出了以下建议:

注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。

也就是说,当你关注问题的抽象层级在DOM元素的时候,你就应该考虑使用自定义指令来完成需求了。

作用范围

自定义指令有两种作用范围,一种是申明在全局,全局内有效。另一种,则是局部指令。

钩子函数及相关参数见官网文档。

举个例子

需求

我需要设计一个自定义指令v-bug,该指令可将作用元素的所有内容都替换为烫烫烫烫,指令可以指定有几个烫,以及是否追加锟斤拷

1. 先将内容替换为一个

main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

Vue.directive('bug', {
  inserted: function (el) {
    el.innerHTML = "烫"
  }
})

new Vue({
  render: h => h(App),
}).$mount('#app')
App.vue
<template>
  <div id="app">
    <HelloWorld v-bug />
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

效果展示

2020-09-20T05:28:47.png

2. 生成指定个数的

main.js
...
Vue.directive('bug', {
  inserted: function (el, binding) {
    el.innerHTML = Array(binding.value).fill("烫").join("")
  }
})
...
App.vue
<template>
  <div id="app">
    <HelloWorld v-bug="12" />
  </div>
</template>

2020-09-20T05:29:25.png

3. 指定是否追加锟斤拷

main.js
...
Vue.directive('bug', {
  inserted: function (el, binding) {
    let html = Array(binding.value).fill("烫").join("")
    if(binding.modifiers.suffix){
      html += "锟斤拷"
    }
    el.innerHTML = html
  }
})
...
App.vue
<template>
  <div id="app">
    <HelloWorld v-bug.suffix="12" />
  </div>
</template>

2020-09-20T05:30:18.png

但是此时的代码有个非常严重的问题,从上文代码中可看出,内容的改变只在inserted的时候执行。如果我们将指令后面设置为变量,变量值的改变却不会影响到个数(这一点都不Vue),所有我们应再监听一下指令的updated钩子函数。

4. 数据驱动(动态改变)

App.vue
<script>
import HelloWorld from "./components/HelloWorld.vue";

let interval = null
export default {
    name: "App",
    components: {
        HelloWorld
    },
    data() {
        return {
            num: 12
        };
    },
    created() {
        interval = setInterval(() => {
            this.num++;
        }, 1000);
    },
    beforeDestroy(){
      clearInterval(interval)
    }
};
</script>
main.js
...
function generateHtml(binding){
  let html = Array(binding.value).fill("烫").join("")
  if (binding.modifiers.suffix) {
    html += "锟斤拷"
  }
  return html
}
Vue.directive('bug', {
  inserted: function (el, binding) {
    el.innerHTML = generateHtml(binding)
  },
  update: function (el, binding) {
    if (binding.oldValue !== binding.value) {
      el.innerHTML = generateHtml(binding)
    }
  },
})
...

2020-09-11T14:22:08.png
需求完成!

2020-08-24T14:58:17.png

引言

总所周知,js是单线程的。单线程意味着,js代码在执行的任何时候,都只有一个主线程来处理所有的任务(为了避免多线程可能产生的诸多问题,直接阉割多线程的可能性)。然而,随着硬件设备的快速发展,网页要做的事情会变得越来越多。人们慢慢意识到,不能让单线程限制了js的效率,然后web worker便应运而生。

栈,堆,队列

2020-08-24T14:58:35.png

基本数据结构知识略过~

讲讲它们在js语言层面的应用。

执行栈:函数调用形成了一个由若干帧(执行上下文)组成的栈。

消息队列:一个待处理消息的消息队列。每一个消息都关联着一个用以处理这个消息的回调函数。

在 事件循环 期间的某个时刻,运行时会从最先进入队列的消息开始处理队列中的消息。被处理的消息会被移出队列,并作为输入参数来调用与之关联的函数。正如前面所提到的,调用一个函数总是会为其创造一个新的栈帧。
函数的处理会一直进行到执行栈再次为空为止;然后事件循环将会处理队列中的下一个消息(如果还有的话)。

任务队列

在js事件循环机制中,存在多种任务队列,它们可分为宏任务(macro-task)和微任务(micro-task)两种。

  • 宏任务包括:setTimeout、setInterval、I/O、UI rendering
  • 微任务包括:process.nextTick、Promise、Object.observe(已废弃)、MutationObserver(html5新特性)

事件循环

2020-08-24T14:58:17.png

如上图所示,事件循环就是在主线程清空执行上下文栈后空闲之时,先去微任务队列中读取待执行程序,并装载到主线程中。如果没有内容(各类微任务队列都清空),则将去宏任务队列中寻找。如此往复,周而复始,直至结束。

参考文档

并发模型与事件循环-MDN
详解JavaScript中的Event Loop(事件循环)机制-知乎
JS事件循环机制(event loop)之宏任务/微任务-掘金
JavaScript 事件循环机制-掘金

引言

相信大家平常在开发过程中,经常会遇到此类需求。危险操作(如删除,退出登录)往往需要弹框提示给用户二次确认一下。本文介绍的是,如何使用js方法装饰器,封装二次确认弹框操作,增加小伙伴的开发效率。

装饰器介绍

2020-08-03T08:01:10.png

装饰器是对类、函数、属性之类的一种装饰,可以针对其添加一些额外的行为。详细使用,自行百度!

常规手段

view

<div class="bg-white flex justify-center">
    <div class="btn-primary" @click="clickMe">点我</div>
</div>

js

clickMe() {
    uni.showModal({
      title: "点击提示",
      content: "你确定要点我?",
      success: (res) => {
        if (res.confirm) {
          console.log("点我干啥");
        }
      },
    });
 }

效果展示

装饰器点击效果

装饰器写法

confirm.js

function confirm(message: string, title: string, cancelFn = function() {}) {
  return function(target: any, name: string, descriptor: any) {
    const originFn = descriptor.value;
    descriptor.value = function(rest: any) {
      uni.showModal({
        title: title,
        content: message,
        success: (res) => {
          if (res.confirm) {
            originFn.apply(this, rest);
          }
        },
      });
    };
  };
}

main.js

@confirm("你确定要点我?","点击提示")
  clickMe() {
    // uni.showModal({
    //   title: "点击提示",
    //   content: "你确定要点我?",
    //   success: (res) => {
    //     if (res.confirm) {
    //       console.log("点我干啥");
    //     }
    //   },
    // });
    console.log("点我干啥");
  }

效果展示

装饰器点击效果

2020-07-17T08:19:34.png

引言

干前端工作,大致离不开三大任务:切图,对接口,写页面逻辑。说到对接口,那肯定是离不开网络请求API的封装的。我将网络请求的封装模式大致分为三个派系:

  1. 无拘无束派 (只封装请求根地址,想咋请求就咋请求,最强的封装就是不封装)
  2. 拦截请求派 (使用一个拦截器配置请求行为和一些错误的拦截处理)
  3. 接口集成派(使用一个或多个文件,统一管理所有请求,约定不允许使用文件中未定义的接口)。

我属于第三个派系,首先介绍一下各派的风格

各派风格

无拘无束派在快速成型方面略有优势,自由度也相对较高,但是维护起来并不容易,遇到接口改版的时候一部小心就会遗漏。

img

拦截请求派属于较优雅的一个派系,没有太多多余的内容,剩下的内容都是为解决问题而生。一般使用一个第三方请求库(如axios,flyio等)完成封装。大致像是这样

import { FlyError, FlyResponse } from "flyio";
const Fly = require("flyio/dist/npm/wx");
let fly = new Fly();
// 配置请求根地址
fly.config.baseURL = process.env.VUE_APP_BASE_URL;
// 配置响应拦截器
fly.interceptors.response.use(
   // 如果请求成功,即请求状态码2xx
  (response: FlyResponse) => {
    // 并且操作成功
    if (response.data.success) {
       // 返回响应数据体
      return Promise.resolve(response.data);
    } else {
    // 请求成功,但是操作失败,提示后端返回的msg,并抛出错误
      uni.showToast({
        icon: "none",
        title: response.data.msg,
      });
      return Promise.reject(response.data);
    }
  },
  // 如果请求失败,即状态非2xx
  (err: FlyError) => {
    console.error(err);
    // 状态码为401,即跳转登录
    if (err.status === 401) {
      uni.reLaunch({
        url: "/pages/login",
      });
      return;
    }
    // 其他错误状态码,就先弹个框吧
    uni.showModal({
      title: err!.request!.url + "接口状态" + err.status,
      content: "错误原因:" + err.engine.response.msg,
    });
    return Promise.reject(err);
  }
);

然后使用的时候就直接采用第三方的请求,如fly.get(),fly.post

现在来介绍我所属的派系,我比上面更极端一点,除了封装请求拦截器暴露POST,GET外,还将所有的接口集中到一个API文件(太多了就按类型拆分,如user,shop,setting)

我的实现

基本请求文件http.ts,已引入上文提及的拦截器

function GET(url: string, params = {}): Promise<ApiResponse> {
  let config = {
    headers: {
      Authorization: store.state.token,
    }
  };
  return fly.get(url, params, config);
}
function POST(url: string, params = {}): Promise<ApiResponse> {
  let config = {
    headers: {
      Authorization: store.state.token,
    }
  };
  return fly.post(url, params, config);
}
export { GET, POST };

api.ts,负责集成Api

import { GET, POST } from "./http";
export default class Api {
  /**
   * 登录接口
   * @param params phone,password
   */
  login(params: object) {
    return POST("/user/login", params);
  }
  /**
   * 获取账户下所有店铺
   */
  getShop() {
    return POST("/shop/getShopListByUserId");
  }
  /**
   * 获取首页数据总览
   * @param params {shopId}
   */
  getOverview(params: {}) {
    return POST("/user/homepage/statistics", params);
  }
  /**
   * 根据店铺Id获取二级分类
   * @param params {shopId}
   */
  getCommodityType(params: {}) {
    return POST("/cnccommodity/commodity_type/by_shop", params);
  }
  /**
   * 分页获取商品列表
   */
  getCommodityList(params: {}) {
    return POST("/cnccommodity/commodity_by_page", params);
  }
}

然后将Api文件,挂载到Vue的原型链。

import API from "./plugins/fly/api";
Vue.prototype.$api = new API();

在组件中使用

 this.$api.login(this.loginForm).then((res) => {
 console.log(res)
 })

这样高度封装的好处是,任何一个接口要改内容,或者自定义功能,都可以只维护Api文件就行了。假设有一需求,要在获取商品之后提示用户”恭喜发财“,而这个接口又在多个页面中使用,我可以只做如下修改即可。

 /**
   * 分页获取商品列表
   */
  getCommodityList(params: {}) {
    showToast("恭喜发财")
    return POST("/cnccommodity/commodity_by_page", params);
  }

这是一个荒诞的需求,但是如果需求是部分请求超过1s就显示加载中呢?或者部分接口需要显示后端返回的msg呢?我可以这样做。

api.ts

updatePw(params: object) {
    return Post("/adminmanage/updatePassWard", params, true);
},

http.ts

export async function Post(api: string, params = {}, needToast = false) {
  let data = await axios.post(api, params);
  if (data.success && needToast) {
    Message.success(data.msg);
  }
  return data;
}

为了应对频繁更改的需求,我真是煞费苦心。

Typescript的加持

上面一直在说接口封装的事情,好像对Typescript只字未提。虽然上面都使用了typescript但是都只是铺垫,真正让代码编写柔润丝滑的是声明文件。

原型Api的声明

上面我们已经将api挂载到vue的原型,但是typescript的作用并未完全发挥。typescript有两个强大的作用,1. 减少代码出错率 2. 提高代码书写效率。要启用ts强大的语法提示功能,我们需要先写一个d.t文件。

vue-property.d.ts

import Vue from "vue";
import Api from "./plugins/fly/api";
declare module "vue/types/vue" {
  interface Vue {
    $api: Api;
  }
}

该文件的作用就是将Vue原型链上的$api类型设置为Api Class, 接下来我们来一起看看它的效果。

typescript推导api

Vs code贴心的语法提示,从注释提示到参数。无与伦比的代码护航能力几乎能让你无脑写请求。

响应结构体的声明

人不能满足现状,光有请求推导可还不够。我们还要让它推导响应结构体。假设项目后端返回的响应结构如下

{
    "code": 200,
    "success": true,
    "msg": "操作成功",
    "result": {
    }
}

我们再创建一个响应的d.ts文件

index.d.ts

interface ApiResponse {
  /**
   * code: 响应状态码
   */
  code: number;
  /**
   * success: 操作是否成功标准
   */
  success: boolean;
  /**
   * msg: 请求的附带信息
   */
  msg: string;
  /**
   * result: 请求返回结果
   */
  result: Object | any;
}

同时声明接口请求返回为ApiResponse

function GET(url: string, params = {}): Promise<ApiResponse> {
  let config = {
    headers: {
      Authorization: store.state.token,
    }
  };
  return fly.get(url, params, config);
}
function POST(url: string, params = {}): Promise<ApiResponse> {
  let config = {
    headers: {
      Authorization: store.state.token,
    }
  };
  return fly.post(url, params, config);
}
export { GET, POST };

看看语法提示效果

ts推导响应体

同时如果有分页,还可以声明分页的结构体。总之,声明文件写得好,效率提高绝对少不了。

后记

ts给我一种以前写C++的感觉,需要先写声明文件.h然后在写.cpp,这样做的好处是约束你的代码,让你的代码更规范。不过,缺点就是声明文件有种给自己找事的感觉,不过我依旧强烈建议在前端项目下使用ts。这在后期的维护是绝对有利的,而且也并非所有文件都需要写声明文件,要不要写声明文件,取决于你的实现方式。比如本文中的Api文件就没写声明文件,照样可以类型推导,语法提示。祝我早日实现无脑编码~

Vue环境变量和模式

Vue环境变量和模式

我最开始使用vue的判断环境的方法是

let BASE_URL = process.env.NODE_ENV == "development" ? "http:/localhost" : "xxx"

在这种情况下,需要先判断它的编译环境,进而根据环境适用对应的值。如果只有一个地方的变量跟环境有关,情况还好。但是若有多个,则需要复制多个判断或者封装成一个方法。但也只能使用于两个环境,如果存在多个环境,诸如开发,测试,预发布,正式等环境,这个方法就捉襟见肘了。所以,我们需要设置Vue环境变量和模式,好像webpack也可以实现?(好吧,这不是重点)

实现方式

vue允许你在项目根目录下添加.env.[mode]的文件来指定环境变量。

.env                # 在所有的环境中被载入
.env.local          # 在所有的环境中被载入,但会被 git 忽略
.env.[mode]         # 只在指定的模式中被载入
.env.[mode].local   # 只在指定的模式中被载入,但会被 git 忽略

比如我,我添加了.env.local,.env.development,.env.test,.env.production等文件,.env.local文件内容如下。

.env.local
VUE_APP_BUILD_MODE = 'development'
VUE_APP_BASE_URL = 'http://172.16.6.132:8002/threemiju/'

对应package.json的script如下:

"scripts": {
    "serve": "vue-cli-service serve --mode",
    "serve:test": "vue-cli-service serve --mode test",
    "build:test": "vue-cli-service build --mode test",
    "build:production": "vue-cli-service build --mode production",
    "deploy:test": "git push && git push gitlab dev && vue-cli-service build --mode test"
  },
请注意,此处有一个坑。当你使用yarn run build:test的时候,你会发现,假如你使用了异步组件,却会神奇的发现,它并没有把把代码切割,只编译成了一个app.js文件,这是由于你使用了--mode test, 导致它认为你并不是prodution环境。所以,你需要对.env.test做以下修改
  • .env.test
NODE_ENV = 'production'
VUE_APP_BUILD_MODE = 'test'
VUE_APP_BASE_URL = 'https://m.gileey.cn/threemiju/'

主动设置NODE_ENV即可。

环境变量的使用场景

  1. BASE_URL
export const baseURL = process.env.VUE_APP_BASE_URL;
  1. BUILD_MODE

在显示版本号时同时显示编译环境

<span :title="mode">{{ version }}</span>
data(){
return {mode: process.env.VUE_APP_BUILD_MODE}
}

本地变量(*.local)的一些应用场景

  1. BASE_URL: 在不同的开发机器下,请求不同内网地址
  2. APP_SECRET: 该字段不宜出现在共有仓库

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写法差别看起来不大的样子。。。

未完待续