1, the preface

Yes, it is also a copy of netease Cloud, so many netease cloud projects also write? It’s all about learning.

Vue3, Vite2 and TypeScript have not been available for new projects. I can’t control my hands and have to write them, in order to read the source code and get familiar with the syntax.

The overall code is more concise, the function is more concise, want to practice can continue to expand, many functions are not done.

The project address

Github.com/haiweilian/…

I don’t know how to design the project UI (and some of the logic). Here is a reference to SSH’s open source project

Github.com/sl1673495/v…

Interface used by Binaryify big bro’s open source project.

Github.com/Binaryify/N…

2. Project preparation

After the content is the development of the process of conveniently recorded notes sorted out, to this step can run to write the project.

extension

  • Initialize a project using Vite.

  • Install VSCode Volar for Vue3.

  • Install Vue3’s Chorme extension Vue Devtools 6.x supports both versions 2 and 3.

Project depend on

"dependencies": {
  "@vueuse/core": "5.1.0"."axios": "^ 0.21.1"."dayjs": "^ 1.10.5"."element-plus": 54 "^ 1.0.2 - beta."."lrc-kit": "^ 1.1.1"."vue": "^ 3.1.4." "."vue-lazyload-next": "^ hundreds." "."vue-router": "^ 4.0.10"."vuex": "4.0.2."
}
Copy the code

Unified plug-in registration

Put all the plug-ins in the Modules directory and use Vite’s import.meta.globEager to load the registration.

All files in modules return a install function.

// src/modules/element-plus.ts
import type { App } from "vue";
import "element-plus/lib/theme-chalk/base.css";

export const install = (app: App) = > {
  app.config.globalProperties.$ELEMENT = { size: "mini" };
};
Copy the code

Import the unified registry using Glob in main.ts.

// src/main.ts
Object.values(import.meta.globEager("./modules/*.ts")).map(i= >i.install? .(app));Copy the code

Vue3

The script Setup syntax used for the whole thing was not finalized at the time of writing and was only partially incompatible after it was finalized. See Script Setup for details.

All dependencies need to be updated and replaced with new syntax. Update dependencies It is recommended to use npm-check-updates to update the entire project.

For example, the changes involved:

  • DefineEmit = > defineEmits.

  • UseContext () -> useSlots() + useAttrs().

  • defineEmits and defineProps no longer need to be imported.

Vuex4

Vuex4 doesn’t change much, except that basically any changes to TS support, such as Store, Commit, dispatch, are not very good hints.

There is a bit of speculation about store in Vue3 that Vuex’s useStore has full state and modules types, but that has to be dealt with separately.

For commit and dispatch, the type is string.

export interface Dispatch {
  (type: string, payload? :any, options? : DispatchOptions):Promise<any>; <P extends Payload>(payloadWithType: P, options? : DispatchOptions): Promise<any>; } export interface Commit { (type: string, payload? : any, options? : CommitOptions): void; <P extends Payload>(payloadWithType: P, options? : CommitOptions): void; }Copy the code

Finally, I am looking forward to vex5, and then I will try to change the version with Pinia.

VueRouter4

The routing part of the change is quite a lot of functionality removed, but most of the removed functionality can be done using custom and V-slot.

For example, using arbitrary tag jumps:

<RouterLink v-slot="{ navigate, isExactActive }" :to="menu.link" custom>
  <li class="menu-song__item" :class="{'is-active': isExactActive}" @click="navigate">
    <Icon :name="menu.icon" />
    <span class="menu-song__value"> {{ menu.name }} </span>
  </li>
</RouterLink>
Copy the code

TypeScript

After reading the tutorial on the official website, there is basically no problem in writing business. There are several main points in combining with Vue.

Import type Uses type to specify the import type, which is fine if not added to xx.ts, but in Script Setup because top-level variables are automatically collected, “PropType” is only the type, but is used here as a value. Using type also makes it easy to distinguish logic from type.

import { onMounted, ref, watch } from "vue";
import type { PropType } from "vue";
Copy the code

We can’t avoid using library-defined types in our projects, so we can click on them based on the function being called and look at the declaration relationships to find subtypes that are not specified in the documentation.

import { ElLoading } from "element-plus";
import type { ILoadingInstance } from "element-plus/packages/loading/src/loading.type";

let needLoadingRequestCount = 0;
let loading: ILoadingInstance;
Copy the code

VueUse

This library was used in this project and it definitely makes Vue3 feel good.

For example, the use of useStorage, onClickOutside, useMediaControls greatly convenient development.

3. Code specification

Coding standards

The integration of various specifications is not a hassle, is directly using the antFU tycoon extracted from the common configuration. It is a combination of rules and plugins to form a set of plugins, don’t want to toss can be used quickly. You can encapsulate a common set of configurations in this way.

npm i eslint @typescript-eslint/eslint-plugin @antfu/eslint-config --save-dev
Copy the code

In the.eslintrc file, add the following. Get formatting for ESLint & typescript & vue3 & React.

{
  "extends": "@antfu"."rules": {
    "no-unused-vars": "off"."@typescript-eslint/no-unused-vars": "off"}}Copy the code

Submit specifications

The commit specification uses husKY, CommitLint, Commitizen, and standard-Version configurations as well. Previously summarized various configurations to facilitate the use of coding specifications, submission specifications.

4. CSS naming

The naming style uses the BEM specification, which uses the mixins function in the elemental-plus source code. For details, see Elemental-plus /theme-chalk.

Using SCSS global import in Vite, you can import file paths. Notice the semicolon (;)

export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: '@import "src/styles/additional.scss"; '}}}});Copy the code

Let’s briefly talk about the main mixins in element-Plus source code: @mixin B ($block), @mixin E ($Element), @mixin M ($Modifier), @mixin when($state).

@mixin b($block)

Define a build block. The parameter is the name of the block.

@include b(input) {
  display: inline-block;
}
Copy the code
.el-input {
  display: inline-block;
}
Copy the code

@mixin e($element)

Define the generated elements. The parameter is the name of the element and can be passed in more than one.

@include b(input) {
  @include e(inner) {
    padding: 0 15px;
  }

  @include e((suffix, suffix-inner)) {
    position: absolute; }}Copy the code
.el-input__inner {
  padding: 0 15px;
}

.el-input__suffix..el-input__suffix-inner {
  position: absolute;
}
Copy the code

@mixin m($modifier)

Define the build modifier. The parameter is the name of the modifier and can be passed in more than one, ($modifier1, $modifier2…) .

@include b(input) {
  @include m(medium) {
    height: 30px;
  }

  @include m((mini, small)) {
    height: 20px; }}Copy the code
.el-input--medium {
  height: 30px;
}

.el-input--mini..el-input--small {
  height: 20px;
}
Copy the code

@mixin when($state)

Define the condition state. The parameter is the name of the state.

@include b(input) {
  @include when(disabled) {
    cursor: not-allowed; }}Copy the code
.el-input.is-disabled {
  cursor: not-allowed;
}
Copy the code

5. SVG ICONS

Working with SVG alone is intended to be used as a component, making it easy to switch between states. Use vite-plugin-SVG-icons to do the processing, the use of the document can be viewed.

After configuring the dependencies and Icon directory, create an Icon component.

<template>
  <svg :style="getStyle" class="icon" aria-hidden="true">
    <use :xlink:href="symbolId" />
  </svg>
</template>

<script setup lang="ts">
import { computed } from "vue";
import type { CSSProperties } from "vue";

const props = defineProps({
  prefix: {
    type: String,
    default: "icon",
  },
  name: {
    type: String,
    required: true,
  },
  size: {
    type: [Number, String],
    default: 16,
  },
});

const symbolId = computed(() => `#${props.prefix}-${props.name}`);
const getStyle = computed((): CSSProperties => {
  const { size } = props;
  let s = `${size}`;
  s = `${s.replace("px", "")}px`;
  return {
    width: s,
    height: s,
  };
});
</script>
Copy the code

Import or global registration can be used.

<Icon :name="menu.icon" />
Copy the code

6. Request processing

After going through the interface, I found that the data processing level is not good for rendering (because it is easy to do, most of them are not used). Therefore, all fields are uniformly converted using map() before being used.

Take a simple 🌰 :

// The field is namea
[{ namea: "lian" }].map(user= > {
  name: user.namea;
});

// The field is nameb
[{ nameb: "lian" }].map(user= > {
  name: user.nameb;
});

// After the transformation is consistent, some deep level also directly drawn level.
[{ name: "lian" }];
Copy the code

All interfaces are written to REST Client configuration files.

@hostname = http://localhost:3000

# Query popular comments of corresponding resourcesGET {{hostname}}/comment/hot? id=186016&typeHTTP / 1.1 = 0# query the comment of the corresponding resourceGET {{hostname}}/comment/new? id=186016&typeHTTP / 1.1 = 0 & sortType = 3Copy the code

7. Analysis of lyrics

Lrc-kit will return the parsed array. After parsing, we can loop directly. What we need to do is to locate the lyrics and automatically scroll to the center.

Using LRC-kit, you can get the number of lines at the current time by using curIndex(), get the offset of the element where the lyrics are, and calculate the scrolling distance from the Ref array in V-for.

/** * get the lyric list ref and position the lyric in the middle of the content when the current row change is detected */
const scroller = ref()
const lyricLineRefs = ref<HTMLElement[]>([])
const setItemRef = (el: HTMLElement): void= > {
  lyricLineRefs.value.push(el)
}

watch(lineActive, (num: number) = > {
  const curDom = lyricLineRefs.value[num]
  scroller.value.scrollTop = curDom.offsetTop - 130 + curDom.offsetHeight / 2
})
Copy the code

8. Project deployment

Both projects are deployed on Vercel.

  • Front-end project Deployment

  • Back-end project deployment

9. Conclusion

Thanks for reading, like ✨✨

After this practice, there should be no problem in writing business. Some advanced types of Ts are used sparingly. Then spare time to study Ts advanced types and Vue3 source code.