开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情
引言
先来做那个安卓播放器的前端部分。最近有点事多,再加上昨天阳了刚退烧,所以在这个项目的制作上并没有特别注重页面的美化与设计,大家简单看看实现就好,前端技术栈方面我采用vite+vue3+typescript+axios的实现。
准备与配置
首先输入
npm init vite@latest
创建一个基于vite的项目
然后因为我个人比较熟悉Vue,所以我选择Vue3作为项目技术选择
然后选择TS(对比过于自由奔放的js,我个人还是喜欢ts,不会ts的小伙伴也可以选择js作为开发语言)
选择vite的优势
这里之所以选择了vite,主要是因为其下面的几个特点与优势。
传统的webpack的不足之处
在讲解vite的优势之前我们先看一下传统的技术栈:webpack的构建的过程。
webpack在build的时候,遇到一些所需的依赖,会跳转到依赖处去分析理解依赖的内容,再回来继续往后构建,而如今前端工程化后,组件间的依赖往往非常复杂,繁琐,webpack在不断的跳转的去构建所额外需要的依赖的时候就会花费大量的时间,导致打包时间也会成倍的增加。
而现在很多项目往往都有复杂的依赖关系,这也导致其越来越慢。
vite的优势
原因一
webpack出现的比较早,那时候浏览器并不支持模块化开发,并不支持ES Modules的标准,那时候的浏览器采用的加载顺序就是从上往下的同步加载。
所以webpack需要把所有的文件都遍历完成,理清依赖关系,定义每个模块的局部变量和全局变量的覆盖顺序,打包成完整的文件后才能交给浏览器去进行加载。
而ES Modules是一种将JavaScript程序拆分为可按需导入的单独模块的机制。
这是vite之所以这么快的第一个原因。
原因二
vite在启动的时候分为 冷启动 和 热更新。
冷启动的快
我们先来说他在冷启动下的快。
Vite在冷启动的时候,将代码分为依赖和源码两部分,源码部分使用ESModules或者CommonJS拆分到很多个小的模块中,
对于依赖部分,Vite使用Esbuild对依赖进行预构建。
Esbuild使用Go语言开发,相对于JavaScript,Go语言是一种编译型语言,在编译阶段就已经将源码转译为机器码。所以比js要快很多很多。
既然底层是大名鼎鼎的Go语言,所以ES Build相比单线程异步的js,他的运行速度和运行效率是要比js快很多的。
Rollup和webpack都没有使用多线程的能力,而ES Build不同,它使用了多线程的优势。
所以也导致了Esbuild构建模块的速度比webpack快到10-100倍。
对于源码部分
Vite省略了webpack遍历打包的时间,这部分工作交给浏览器,基本没有打包的时间,Vite只是在浏览器发送对模块的请求时,拦截请求,对源码进行转换后提供给浏览器,实现了源码的动态导入。
热更新的快
vite在热更新上,不需要对所有的改动都完全重构打包,vite只会对失活的模块进行热重载,而不影响页面的其他部分。
无论项目的规模多大,Vite的热更新也是在原生的ESM上进行的,只会加载当前使用到的模块。
vite的缺陷
Vite的生态与webpack相差甚远,在plugin等方面无法媲美诞生时间久远的webpack。
Vite的开发环境很惊艳,但是因为一些原因,生产环境还是使用Rollup进行构建的,还是需要打包的。
题外话结束,开始正式进入项目的配置中去
首先我们需要下载axios与vue-axios的依赖,然后在配置文件中完成使用与挂载。
main.ts配置
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import axios from 'axios';
import VueAxios from 'vue-axios';
const app = createApp(App);
app.use(VueAxios,axios);
app.config.globalProperties.$axios = axios;
app.mount("#app");
一个大坑
我们主要来介绍这个项目的一个小坑,项目的实现,想必对于开始学习vite,vue3和ts的小伙伴应该不会太难,即使是有所遗漏,在后面的代码看完之后也会很快的理解。
我们在<script setup lang="ts">内启动了ts,但是随之而来的有一些问题,比如我们初始化proxy用的的getCurrentInstance(),在ts中会因为返回值的问题出现error,那么我们应该如何解决呢?
vue3项目中,如果不用ts这样使用是没问题的
const { proxy } = getCurrentInstance()
但是在ts中则会报错。
...类型“ComponentInternalInstance | null
所以我们要对其进行封装使用。
import { ComponentInternalInstance, getCurrentInstance } from 'vue'
export default function useCurrentInstance() {
const { appContext } = getCurrentInstance() as ComponentInternalInstance
const proxy = appContext.config.globalProperties
return {
proxy
}
}
封装好的如上,这样我们使用的时候只需要导入
import useCurrentInstance from './useCurrentInstance';
然后直接调用useCurrentInstance完成创建即可。
最后简单看一下代码吧
<template>
<div class="main">
<h2>
安卓音乐播放器
</h2>
<div>
这是一款完全免费的音乐播放器
</div>
<main>
<div>
<h3>音乐列表</h3>
<p v-for="it in menuList" @click="sendIdTo(it.name)">
{{it.id}}-{{it.name}}----{{it.author}}
</p>
</div>
<audio controls ref="audioRef">
<source id="source" :src='urlSend' type="audio/mpeg">
</audio>
</main>
</div>
</template>
<style scoped>
.main {
display: flex;
justify-content: center;
flex-direction: column;
height: 100%;
width: 100%;
}
</style>
<script setup lang="ts">
import { ref , reactive , getCurrentInstance , onBeforeMount , ComponentInternalInstance , Ref } from "vue";
import useCurrentInstance from './useCurrentInstance';
let { proxy } = useCurrentInstance();
interface MenuList {
id?:number;
name?:string | undefined;
author?:string
}
let menuList:Ref<MenuList[]> = ref([{}]);
onBeforeMount(() => {
//调用方法
proxy.$http
.get("http://localhost:8083/musicList")
.then(function(res):void {
console.log(res.data);
menuList.value = res.data;
console.log(menuList);
})
.catch(function(error) {
console.log(error);
});
});
let urlSend:Ref<string> = ref("");
let audioRef = ref(null)
function sendIdTo(n:string|undefined):void {
console.log(n);
urlSend.value = "";
urlSend.value = urlSend.value + "http://localhost:8083/music?musicName=" + n;
console.log(urlSend.value);
(audioRef.value as any).load();
(audioRef.value as any).play();
}
</script>