21
头图

During May 1st, I spent 3 days learning Vue3 and Vite2 while refactoring my own projects, and finally rebuilt them with Vue3 + TypeScript + Vite2 + Vuex4 + Vue-Router4 + element-plus!

Finally completed a 2021 annual goal that I have been thinking about ✌️

project address:

https://github.com/biaochenxuying/blog-vue-typescript

effect

Effect picture:

  • pc side

  • Mobile

See the full effect:

https://biaochenxuying.cn

Features

Function has been completed

  • [x] Login
  • [x] Register
  • [x] Article list
  • [x] Article Archive
  • [x] tag
  • [x] About
  • [x] Likes and comments
  • [x] Message
  • [x] History
  • [x] Article details (support code syntax highlighting)
  • [x] List of article details
  • [x] Mobile end adaptation
  • [x] github authorized login

⬆️ back to top

Main front-end technology

All technologies are currently up-to-date.

  • vue:^3.0.5
  • typescript : ^4.1.3
  • element-plus: ^1.0.2-beta.41
  • vue-router : ^4.0.6
  • vite: ^2.2.3
  • vuex: ^4.0.0
  • axios: ^0.21.1
  • highlight.js: ^10.7.2
  • marked:^2.0.3

1. Initialization project

Create project with vite-app

yarn create vite-app <project-name>

# 或者
npm init vite-app <project-name>

Then just follow the prompts!

Enter the project, install dependencies

cd <project-name>

yarn # 或 npm i

Run the project

yarn dev 

Open the browser http://localhost :3000 view

2. Introduce TypeScript

TypeScript can be used when creating the project. If you choose TypeScript, you can ignore the second step.

Add ts dependency

yarn add --dev typescript

Create TypeScript configuration file tsconfig.json in the project root directory

{
  "compilerOptions": {
    // 允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。
    "allowSyntheticDefaultImports": true,
    
    // 解析非相对模块名的基准目录
    "baseUrl": ".",

    "esModuleInterop": true,

    // 从 tslib 导入辅助工具函数(比如 __extends, __rest等)
    "importHelpers": true,

    // 指定生成哪个模块系统代码
    "module": "esnext",

    // 决定如何处理模块。
    "moduleResolution": "node",

    // 启用所有严格类型检查选项。
    // 启用 --strict相当于启用 --noImplicitAny, --noImplicitThis, --alwaysStrict, 
    // --strictNullChecks和 --strictFunctionTypes和--strictPropertyInitialization。
    "strict": true,

    // 生成相应的 .map文件。
    "sourceMap": true,

    // 忽略所有的声明文件( *.d.ts)的类型检查。
    "skipLibCheck": true,

    // 指定ECMAScript目标版本 
    "target": "esnext",
    
    // 要包含的类型声明文件名列表
    "types": [

    ],

    "isolatedModules": true,

    // 模块名到基于 baseUrl的路径映射的列表。
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    // 编译过程中需要引入的库文件的列表。
    "lib": [
      "ESNext",
      "DOM",
      "DOM.Iterable",
      "ScriptHost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

Add a new shim.d.ts file in the src directory

/* eslint-disable */
import type { DefineComponent } from 'vue'

declare module '*.vue' {
  const component: DefineComponent<{}, {}, any>
  export default component
}

Change main.js to main.ts

In the root directory, open Index.html

<script type="module" src="/src/main.js"></script>
修改为:
<script type="module" src="/src/main.ts"></script>

3. Introduce eslint

Install eslint prettier dependency

@typescript-eslint/parser @typescr ipt-eslint/eslint-plugin is eslint's support for typescript.

yarn add --dev eslint prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue @typescript-eslint/parser @typescr ipt-eslint/eslint-plugin

Create an eslint configuration file in the root directory: .eslintrc.js

module.exports = {
  parser: 'vue-eslint-parser',
  parserOptions: {
    parser: '@typescript-eslint/parser',
    ecmaVersion: 2020,
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true
    }
  },
  extends: [
    'plugin:vue/vue3-recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier/@typescript-eslint',
    'plugin:prettier/recommended'
  ],
  rules: {
    '@typescript-eslint/ban-ts-ignore': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/no-var-requires': 'off',
    '@typescript-eslint/no-empty-function': 'off',
    'vue/custom-event-name-casing': 'off',
    'no-use-before-define': 'off',
    // 'no-use-before-define': [
    //   'error',
    //   {
    //     functions: false,
    //     classes: true,
    //   },
    // ],
    '@typescript-eslint/no-use-before-define': 'off',
    // '@typescript-eslint/no-use-before-define': [
    //   'error',
    //   {
    //     functions: false,
    //     classes: true,
    //   },
    // ],
    '@typescript-eslint/ban-ts-comment': 'off',
    '@typescript-eslint/ban-types': 'off',
    '@typescript-eslint/no-non-null-assertion': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-unused-vars': [
      'error',
      {
        argsIgnorePattern: '^h$',
        varsIgnorePattern: '^h$'
      }
    ],
    'no-unused-vars': [
      'error',
      {
        argsIgnorePattern: '^h$',
        varsIgnorePattern: '^h$'
      }
    ],
    'space-before-function-paren': 'off',
    quotes: ['error', 'single'],
    'comma-dangle': ['error', 'never']
  }
};

Create prettier.config.js

module.exports = {
  printWidth: 100,
  tabWidth: 2,
  useTabs: false,
  semi: false, // 未尾逗号
  vueIndentScriptAndStyle: true,
  singleQuote: true, // 单引号
  quoteProps: 'as-needed',
  bracketSpacing: true,
  trailingComma: 'none', // 未尾分号
  jsxBracketSameLine: false,
  jsxSingleQuote: false,
  arrowParens: 'always',
  insertPragma: false,
  requirePragma: false,
  proseWrap: 'never',
  htmlWhitespaceSensitivity: 'strict',
  endOfLine: 'lf'
}

4. vue-router、vuex

npm install vue-router@4 vuex

4.1 vuex

Create store/index.ts in the root directory

import { InjectionKey } from 'vue'
import { createStore, Store } from 'vuex'

export interface State {
  count: number
}

export const key: InjectionKey<Store<State>> = Symbol()

export const store = createStore<State>({
  state() {
    return {
      count: 0
    }
  },
  mutations: {
    increment(state) {
      state.count++
    }
  }
})

main.ts modification

import { createApp } from 'vue'
import { store, key } from './store'
import App from './App'
import './index.css'

const app = createApp(App)

app.use(store, key)

app.mount('#app')

components/HelloWord.vue modification

<template>
  <h1>{{ msg }}</h1>
  <button @click="inCrement"> count is: </button>
  <p>{{ count }}</p>
</template>

<script>
  import { defineComponent, computed } from 'vue'
  import { useStore } from 'vuex'
  import { key } from '../store'

  export default defineComponent({
    name: 'HelloWorld',
    props: {
      msg: {
        type: String,
        default: ''
      }
    },
    setup() {
      const store = useStore(key)

      const count = computed(() => store.state.count)

      return {
        count,
        inCrement: () => store.commit('increment')
      }
    }
  })
</script>

4.2 vue-router

Create router/index.ts in the src directory, the content is as follows:

import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import HelloWorld from "../components/HelloWorld.vue";

const routes: Array<RouteRecordRaw> = [
    {
        path: "/",
        name: "HelloWorld",
        component: HelloWorld,
    },
    {
        path: "/about",
        name: "About",
        // route level code-splitting
        // this generates a separate chunk (about.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        component: () =>
            import(/* webpackChunkName: "About" */ "../components/About.vue")
    }
];

const router = createRouter({
    history: createWebHistory(process.env.BASE_URL),
    routes,
});

export default router;

Create a new components/About.vue file with the following content:

<template>
  <img
    alt="Vue logo"
    src="../assets/logo.png"
  />
  <h1>{{ msg }}</h1>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  name: 'About',
  data() {
    return {
      msg: 'Hello Vue 3.0 + Vite!'
    }
  },
  setup() {}
})
</script>

Modify main.ts again

import { createApp } from 'vue'
import { store, key } from './store'
import router from "./router";
import App from './App'
import './index.css'

const app = createApp(App)

app.use(store, key)
app.use(router)
app.mount('#app')

Visit http://localhost :3000/

And http://localhost :3000/about

5. Join Element Plus

5.1 Install element-plus

global installation

npm install element-plus --save

5.2 Introducing Element Plus

You can import the entire Element Plus, or only part of the components as needed. We first introduce how to introduce a complete Element.

fully introduced

Write the following in main.js:

import { createApp } from 'vue'
import ElementPlus from 'element-plus';
import router from "./router";
import 'element-plus/lib/theme-chalk/index.css';
import App from './App.vue';
import './index.css'

const app = createApp(App)
app.use(ElementPlus)
app.use(router)
app.mount('#app')

The above code completes the introduction of Element Plus. It should be noted that the style file needs to be imported separately.


on demand

With babel-plugin-component , we can introduce only the required components to achieve the purpose of reducing the size of the project.

First, install babel-plugin-component:

npm install babel-plugin-component -D

Then, modify .babelrc to:

{
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-plus",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

Next, if you only want to introduce some components, such as Button and Select, you need to write the following in main.js:

import { createApp } from 'vue'
import { store, key } from './store';
import router from "./router";
import { ElButton, ElSelect } from 'element-plus';
import App from './App.vue';
import './index.css'

const app = createApp(App)
app.component(ElButton.name, ElButton);
app.component(ElSelect.name, ElSelect);

/* or
 * app.use(ElButton)
 * app.use(ElSelect)
 */

app.use(store, key)
app.use(router)
app.mount('#app')
app.mount('#app')

For more detailed installation methods, please see Quick Start .

5.3 Global configuration

When introducing Element Plus, you can pass in a global configuration object.

The object currently supports size and zIndex fields. size used to change the default size of the component, and zIndex sets the initial z-index of the bullet frame (default: 2000). According to the introduction of Element Plus, the specific operations are as follows:

Complete introduction of Element:

import { createApp } from 'vue'
import ElementPlus from 'element-plus';
import App from './App.vue';

const app = createApp(App)
app.use(ElementPlus, { size: 'small', zIndex: 3000 });

Introduce Element on demand:

import { createApp } from 'vue'
import { ElButton } from 'element-plus';
import App from './App.vue';

const app = createApp(App)
app.config.globalProperties.$ELEMENT = option
app.use(ElButton);

According to the above settings, size attribute in the project is'small', and the initial z-index of the bullet frame is 3000.

5.4 Configure vite.config.ts

Among them, proxy and alias are quite different from vue-cli.

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import styleImport from 'vite-plugin-style-import'
import path from 'path'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    styleImport({
      libs: [
        {
          libraryName: 'element-plus',
          esModule: true,
          ensureStyleFile: true,
          resolveStyle: (name) => {
            return `element-plus/lib/theme-chalk/${name}.css`;
          },
          resolveComponent: (name) => {
            return `element-plus/lib/${name}`;
          },
        }
      ]
    })
  ],

  /**
   * 在生产中服务时的基本公共路径。
   * @default '/'
   */
  base: './',
  /**
  * 与“根”相关的目录,构建输出将放在其中。如果目录存在,它将在构建之前被删除。
  * @default 'dist'
  */
  // outDir: 'dist',
  server: {
    // hostname: '0.0.0.0',
    host: "localhost",
    port: 3001,
    // // 是否自动在浏览器打开
    // open: true,
    // // 是否开启 https
    // https: false,
    // // 服务端渲染
    // ssr: false,
    proxy: {
      '/api': {
        target: 'http://localhost:3333/',
        changeOrigin: true,
        ws: true,
        rewrite: (pathStr) => pathStr.replace('/api', '')
      },
    },
  },
  resolve: {
    // 导入文件夹别名
    alias: {
      '@': path.resolve(__dirname, './src'),
      views: path.resolve(__dirname, './src/views'),
      components: path.resolve(__dirname, './src/components'),
      utils: path.resolve(__dirname, './src/utils'),
      less: path.resolve(__dirname, "./src/less"),
      assets: path.resolve(__dirname, "./src/assets"),
      com: path.resolve(__dirname, "./src/components"),
      store: path.resolve(__dirname, "./src/store"),
      mixins: path.resolve(__dirname, "./src/mixins")
    },
  }
})

Step on the pit

No errors were reported when npm run dev npm run build were reported when node_modules . The files in 06094982184f66 were also compiled during build, so many element-plus type files reported errors.

tsconfig.json the include and exclude in 06094982184f7e and it won’t work. The configuration is as follows

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    // 忽略 this 的类型检查, Raise error on this expressions with an implied any type.
    "noImplicitThis": false,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "lib": ["esnext", "dom"],
    "types": ["vite/client"]
  },
  "include": ["/src/**/*.ts", "/src/**/*.d.ts", "/src/**/*.tsx", "/src/**/*.vue"],
  // ts 排除的文件
  "exclude": ["node_modules"]
}

The files packaged by Vue3 + vite2 are quite different from the original vue2 version. The original 2.5M is directly changed to 1.8M, amazing!

At last

Most of the project code is 2 years ago, and there are still many areas that can be optimized. This refactoring process did not make any changes to the original style and code, and there is not so much time, plus I am lazy😂

This time I upgraded the main framework and the corresponding ui library. After going through the APIs in Vue3, I found that many new APIs in Vue3 are not available. The main reason is to get familiar with the construction of Vue3 and Vite2 projects. This holiday is also considered to be somewhat different. reward.

Please see the specific project source code:

https://github.com/biaochenxuying/blog-vue-typescript

At this point, a development environment based on Vue3 family bucket + Vite2 + TypeScript + Element Plus has been set up, and now you can write code. For the usage of each component, please refer to their respective documents.

I have to say that Vue3 + Element Plus + Vite + TypeScript is really fragrant!

Recommend a summary of Vue3 related information: Vue3 learning tutorial summary, source code explanation project, supported UI component library, high-quality actual combat project , I believe you will dig into the mine!

Reference article: vue3 + vite + typescript + eslint + jest project configuration practice

recommended reading

Welcome to pay attention to the public : "16094982185120 full stack practice ", reply to " e-book " to get the following 300 , cat brother wx: CB834301747.


夜尽天明
13.9k 声望6.5k 粉丝