摘要

我一直觉得vue特别难入门,其实不是vue的语法难学,而是vue所附加的其他生态链让人头疼。

由此记录。

正文

实战项目可参考meethigher/code2word: Code2Word - 让高亮的代码完美呈现至 Word

一、Node版本管理

1.1 搭建node

1.1.1 Windows

准备nvm-windows,是windows系统用来管理node版本的工具。使用过node的应该都知道,node新版本是完全不兼容旧版本的,这就是这个工具存在的意义

sh
1
2
3
4
5
6
7
8
9
# 查看列表
nvm list
# 下载 16.16.0
nvm install 16.16.0
# 切换版本
nvm use 16.16.0
# 查看版本
node -v
npm -v

操作如图

image-20230115010007318.png

1.1.2 Linux

保证配置代理的基础上,执行如下命令

sh
1
2
3
4
5
6
7
curl -L -o a.tar.gz https://github.com/nvm-sh/nvm/archive/refs/tags/v0.39.2.tar.gz && tar -zxvf a.tar.gz && mv nvm-0.39.2/ /usr/local/nvm
cat > /etc/profile.d/nvm.sh <<EOF
#!/usr/bin/env bash
export NVM_DIR="/usr/local/nvm"
[ -s "\$NVM_DIR/nvm.sh" ] && \. "\$NVM_DIR/nvm.sh"  # This loads nvm
EOF
source /etc/profile && nvm --version

操作如图

image-20230520184847966.png

参考CentOS7下安装NVM_centos 安装nvm_veejaLiu的博客-CSDN博客

1.2 创建vue项目

参考文档简介 | Vue.js,执行命令

sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
C:\Users\meethigher\Desktop>npm init vue@3.4
npm WARN config global `--global`, `--local` are deprecated. Use `--location=global` instead.

Vue.js - The Progressive JavaScript Framework

√ Project name: ... vue-admin
√ Add TypeScript? ... No
√ Add JSX Support? ... No
√ Add Vue Router for Single Page Application development? ... Yes
√ Add Pinia for state management? ... No
√ Add Vitest for Unit Testing? ... No
√ Add an End-to-End Testing Solution? » No
√ Add ESLint for code quality? ... No

Scaffolding project in C:\Users\meethigher\Desktop\vue-admin...

Done. Now run:

  cd vue-admin
  npm install
  npm run dev

按提示命令,启动项目

image-20230101211827724.png

二、项目理解

2.1 配置IDE

作为一个后端人员来说,还是更喜欢jetbrains家的产品,此处使用的webstorm。由于写js习惯性带;,习惯用"",所以需要配置自动添加分号。

进行ctrl+alt+L格式化代码时,即可自动添加。

image-20230101215235189.png

2.2 理解代码

1.) 理解main.js

js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 解构出 createApp
import {createApp} from "vue";
// 导入根组件
import App from "./App.vue";
// 导入路由配置
import router from "./router";
// 导入全局样式
import "./assets/main.css";
// 创建一个根app实例
const app = createApp(App);
// 将路由挂载到app实例
app.use(router);
// 将app实例挂载到id为app的元素上
app.mount("#app");

2.) 理解router.js,参考文档介绍 | Vue Router

js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
* 1. 先导入 vue-router
* createRouter 创建路由的方法
* createWebHistory 路由模式(History) 前面用的是 / 开头,一般是要后端配合使用,容易产生404
* createWebHashHistory 路由模式(Hash) 前面用的是 # 开头,无需配合
* */
import {createRouter, createWebHistory, createWebHashHistory} from "vue-router";
// 导入home组件
import HomeView from "../views/HomeView.vue";

// 配置路由规则
const routers = [
    {
        //访问时的路径
        path: "/",
        //命名路由,路由的别名
        name: "home",
        //当访问到对应路由后需要展示的组件
        //路由的急加载
        component: HomeView
    },
    {
        path: "/about",
        name: "about",
        //路由的懒加载,对于一些不确定立即需要显示的页面,可以使用懒加载
        component: () => import("../views/AboutView.vue")
    }
];

// 创建router
const router = createRouter({
    // 配置路由模式
    history: createWebHistory(import.meta.env.BASE_URL),
    // 配置路由规则
    routes: routers
});

// 导出路由
export default router;

image-20230101214506250.png

3.) 理解app.vue

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<!--
在script标签中,带setup,说明在该script中写的所有代码,就是在setup中写的
-->
<script setup>
    import {RouterLink, RouterView} from "vue-router";
    //在setup中,不需要手动注册组件,直接导入即可使用
    import HelloWorld from "./components/HelloWorld.vue";
</script>

<template>
    <header>
        <div class="wrapper">
            <HelloWorld msg="You did it!"/>

            <nav>
                <!--RouterLink表示声明式跳转-->
                <!--还有编程式跳转 通过js跳转-->
                <!--to属性指定需要跳转的路径-->
                <RouterLink to="/">Home</RouterLink>
                <RouterLink to="/about">About</RouterLink>
            </nav>
        </div>
    </header>
    <!--RouterView:路由出口,所有的组件页面,都可以通过RouterView来展示-->
    <RouterView/>
</template>
<!--
scoped该属性是用来标记当前样式只能在当前组件中使用
其存在的目的是为了防止样式冲突
-->
<style scoped>
    header {
        line-height: 1.5;
        max-height: 100vh;
    }

    .logo {
        display: block;
        margin: 0 auto 2rem;
    }

    nav {
        width: 100%;
        font-size: 12px;
        text-align: center;
        margin-top: 2rem;
    }

    nav a.router-link-exact-active {
        color: var(--color-text);
    }

    nav a.router-link-exact-active:hover {
        background-color: transparent;
    }

    nav a {
        display: inline-block;
        padding: 0 1rem;
        border-left: 1px solid var(--color-border);
    }

    nav a:first-of-type {
        border: 0;
    }

    @media (min-width: 1024px) {
        header {
            display: flex;
            place-items: center;
            padding-right: calc(var(--section-gap) / 2);
        }

        .logo {
            margin: 0 2rem 0 0;
        }

        header .wrapper {
            display: flex;
            place-items: flex-start;
            flex-wrap: wrap;
        }

        nav {
            text-align: left;
            margin-left: -1rem;
            font-size: 1rem;

            padding: 1rem 0;
            margin-top: 1rem;
        }
    }
</style>

三、搭建项目

3.1 置空项目

将不必要的文件都删除,最后的结构如图

image-20230101235937027.png

启动项目后是个空的

image-20230102000104036.png

3.2 安装css预编译less

安装

sh
1
2
# 带上-D参数,表示安装到devDepencencies
npm install less

使用

html
1
2
3
4
<!--css预编译语言:less、sass-->
<!--webstorm不支持sass的代码提示,刚好我也不会-->
<style scoped lang="less">
</style>

3.3 安装全局初始化样式

normalize - npm

安装

sh
1
npm install normalize

在main.js中全局引用

js
1
2
// 导入全局格式化标签样式
import "normalize.css/normalize.css";

3.4 安装element-plus

在element-plus中,元素名恰好是对应的class名称,可以直接配样式

快速开始 | Element Plus

安装

sh
1
npm install element-plus

配置自动按需导入

sh
1
npm install -D unplugin-vue-components unplugin-auto-import

按照官网要求修改vite.config.js

image-20230102005344381.png

3.5 安装element-icon

Icon 图标 | Element Plus安装

sh
1
npm install @element-plus/icons-vue

实际使用时,手动按需引入即可,如下

js
1
import {Fold, Expand} from "@element-plus/icons-vue";

3.6 安装axios

3.6.1 配置axios

Axios安装

sh
1
npm install axios

封装通用request.js

js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import axios from "axios";

//封装baseUrl
//开发环境development 打包命令npm run dev
//生产环境production 打包命令npm run build
const isDev = process.env.NODE_ENV == "development";
const dev = "http://127.0.0.1/api/";
const pro = "http://121.89.205.189:3000/";
const request = axios.create({
    baseURL: isDev ? dev : pro,
    timeout: 60000,//单位毫秒
    headers: {"X-Custom-Header": "foobar"}
});


// 添加请求拦截器
request.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});

// 添加响应拦截器
request.interceptors.response.use(function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    return response;
}, function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error);
});


// 自定义各种数据请求
export default function ajax(config) {
    //解构赋值 https://www.cnblogs.com/tfxz/p/12838739.html
    // let node = {
    //     type: "Identifier"
    // };
    // // 使用解构来创建两个字段,type、name,如果node中该字段有值,则覆盖掉
    // ({type = 'type', name = 'name'} = node);
    // console.log(type); // "Identifier"
    // console.log(name); // "name"
    const {url = "", method = "GET", data = {}, headers = {}} = config;

    //判断请求类型
    switch (method.toUpperCase()) {
        case "GET":
            return request.get(url, {
                params: data
            });
        case "POST":
            //表单形式
            if (headers["content-type"] == "application/x-www-form-url-encoded") {
                //格式化数据
                const obj = new URLSearchParams();
                for (let key in data) {
                    obj.append(key, data[key]);
                }
                return request.post(url, data, {headers});
            }
            //文件形式
            if (headers["content-type"] == "multipart/form-data") {
                //文件处理对象
                let obj = new FormData();
                for (let key in data) {
                    obj.append(key, data[key]);
                }
                return request.post(url, data, {headers});
            }
            //json形式
            return request.post(url, data);
    }
}

3.6.2 配置vite跨域

开发服务器选项 | Vite 官方中文文档

js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
server: {
    port: 80,
    open: false, //自动打开
    base: "./ ", //生产环境路径
    /*配置代理转发,开发环境使用https://www.cnblogs.com/tik2012/p/16866356.html*/
    proxy: { // 本地开发环境通过代理实现跨域,生产环境使用 nginx 转发
        // 正则表达式写法
        '^/api': {
            target: 'http://121.89.205.189:3000/', // 后端服务实际地址
            changeOrigin: true, //开启代理
            rewrite: (path) => path.replace(/^\/api/, '')
        }
    }
}

3.7 安装全局数据管理工具

可选框架,两者二选一即可

  • vuex
  • pinia

在vue2.x中推荐使用vuex,在vue3.x中推荐使用pinia。pinia相比vuex使用起来更简洁。

3.7.1 vuex

配置vuex

Vuex 是什么? | Vuex安装

sh
1
npm install vuex@next

封装通用vuex.js

js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 使用vuex进行全局状态管理

import {createStore} from "vuex";

const store = createStore({
    //严格模式,在生产环境中,不能打开
    strict: process.env.NODE_ENV !== "production",
    //全局状态
    state() {

        return {
            count: 100,
            //用户信息
            userInfo: {}
        };
    },
    //项目中唯一修改数据的方法
    //严格模式中,不允许在这里做异步,要在actions里面做。比如setTimeout等
    mutations: {
        add(state) {
            state.count++;
        },
        addNum(state, amount) {
            state.count += amount;
        },
        //保存用户数据
        updateUserInfo(state, value) {
            state.userInfo = value;
        }
    },
    //异步修改数据的地方
    actions: {
        addAction(context) {
            setTimeout(function () {
                /*所谓异步,也还要调用同步的方法进行修改数据*/
                context.commit("add");
            }, 5000);
        },
        addActionNum(context, amount) {
            setTimeout(function () {
                /*所谓异步,也还要调用同步的方法进行修改数据*/
                context.commit("addNum", amount);
            }, 5000);
        }
    },
    //vuex中的计算属性
    getters: {},
    //全局状态模块
    modules: {}
});


//导出创建好的实例
export default store;

vuex数据持久化

持久化vuex数据,方便在页面重新加载时,恢复数据。

  1. 通过自己手动存储需要持久化的数据
  2. 通过插件进行自动持久化

自动持久化,需要安装vuex插件

sh
1
npm install vuex-persistedstate

添加plugins,如下图

image-20230102030835815.png

使用生命周期函数验证登录

js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
export default {
    //生命周期函数
    mounted() {
        //用户没有登录
        if (!this.$store.state.userInfo.adminname) {
            this.$router.push("/login");
            return;
        }
    }
}

3.7.2 pinia

Vue3项目中推荐使用Pinia,安装

sh
1
npm install pinia

main.js注册pinia

js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// src/main.js
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';

const app = createApp(App);
const pinia = createPinia();

app.use(pinia);
app.mount('#app');

创建Pinia Store

js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/stores/codeStore.js
import {defineStore} from 'pinia'

export const useCodeStore = defineStore('code', {
    state: () => ({
        detectedLanguage: '未识别',
        rawCode: '',    // 原始代码(输入区)
        emitCode:'', // 高亮按钮传递的数据
        highlightedCode: '', // 高亮后的代码(预览区)
        outside: false // 序号是否展示在外侧。word只支持内侧
    }),
    actions: {
        // 更新输入的代码
        setRawCode(code) {
            this.rawCode = code
        },
        // 更新高亮代码(这里可以集成 highlight.js 进行高亮处理)
        setHighlightCode(code) {
            this.highlightedCode = code;
        }
    }
})

在组件中使用

vue
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<template>
  <nav class="navbar">
    <div class="nav-links">
      <a :href="Utils.home" class="link">主页</a>
      <a :href="Utils.blog" class="link">博客</a>
      <a :href="Utils.github" target="_blank" class="link">GitHub</a>
    </div>
  </nav>
</template>

<script setup>
import {Utils} from "./utils/utils";
import logo from "@/assets/logo.svg";
Utils.printProjectInfo();
Utils.printAttention();
</script>