尤大的 Vue3.0 已经发布有一阵子了, 已经很成熟了。今天想起来,是时候该上手体验分享一波了。

前言

大家都知道,在 Vue3 生态系统中有一个新构建工具 Vite ,它的开发服务器比 Vue CLI 快 10 ~ 100倍,是不是很好奇?具体的内容可以移步至:迟到的 Vue3.0 与 Vite 的体验学习总结 的 七、Vite 与 Vue CLI 阅读。

本文将主要分享如何使用 Vite 来创建一个 Vue3.0 的项目,并且结合相关内容(TypeScriptVue RouterVuexAnt Design等)编写一个简单的Demo。


使用 Vite 搭建 Vue3.0 项目

1. 全局安装 Vite

npm install -g create-vite-app

2. 使用 Vite 创建 Vue3 项目

create-vite-app vue3-vite

可以发现瞬间创建完成。

Done. Now run:
  cd vue3-vite
  npm install (or `yarn`)
  npm run dev (or `yarn dev`)

根据提示安装依赖运行项目

C:\Users\jingx\Desktop\vite\vue3-vite>yarn dev
yarn run v1.22.10
warning package.json: No license field
$ vite
[vite] Optimizable dependencies detected:
vue

  Dev server running at:
  > Network:  http://10.7.40.138:3000/
  > Local:    http://localhost:3000/

项目成功启动:
在这里插入图片描述

3. 配置 Vite

在项目中创建一个 vite.config.jsvite.config.ts 文件。如果在当前工作目录中找到 Vite,它将自动使用它。

vite.config.ts 基本配置:

const path = require("path")

function pathResolve(dir: string) {
    return path(__dirname, ".", dir);
}

module.exports = {
    // 服务端渲染
    ssr: false,
    // 是否开启 https
    https: false,
    // 设置目录别名
    alias: {
        // 键必须以斜线开始和结束
        '/@/': pathResolve('/src'),
        '/@components/': pathResolve('/src/components')
    },
    // 跨域设置
    proxy: {
        // 如果是 /api 开头,则访问如下地址
        '/api': 'http://10.7.40.138:8080',
    },
    // 引入第三方的配置
    optimizeDeps: {
        include: ["moment", "echarts", "axios", "mockjs", "@ant-design/icons-vue"]
    },
}

详细 config.ts 配置参考:Vite - config.ts

4. 插件依赖安装

  • eslint-plugin-vue

    yarn add -D eslint eslint-plugin-vue
    
  • TypeScript

    yarn add -D typescript
    

    参考文档:

  • Vue Router

    yarn add vue-router@next
    
  • Vuex

    yarn add vuex@@next
    
  • Ant Design Vue

    yarn add ant-design-vue@next
    
    • 完整引入组件

      import { createApp } from 'vue'
      import App from './App.vue'
      import './index.css'
      import Antd from 'ant-design-vue';
      import 'ant-design-vue/dist/antd.css'; 
      
      createApp(App).use(Antd).mount('#app');
      

      以上代码便完成了 Antd 的引入。需要注意的是,样式文件需要单独引入。

      注意:createApp(App).mount(’#app’).use(Antd)
      .mount(’#app’) 和 .use(Antd) 的顺序一定不要写错,否则会报:createApp(…).mount(…).use is not a function 的错误。
      这也是以为小伙伴提醒我的,非常感谢。

    • 按需引入组件

      # 需要使用 babel-plugin-import 来进行按需加载
      yarn add -D babel-plugin-import
      

      配置 .babelrc 文件

      {
        "plugins": [
          ["import", { "libraryName": "ant-design-vue", "libraryDirectory": "es", "style": "css" }] // `style: true` 会加载 less 文件
        ]
      }
      
      # 按需加载需要的组件
      import { Button } from 'ant-design-vue';
      
  • Sass

    yarn add -D sass
    

使用插件开发完整项目

  • 使用 TypeScript

    • TypeScriptJavaScript 类型的超集,它可以编译成纯 js
    • 我们在项目中要做的就是将 .js 文件更新成 .ts 文件即可,至于里面的细节内容可以去 TypeScript官网 阅读文档学习。
    • 当然,在项目中也可以不使用 ts,其他内容都是相同的,本项目是为了体验 ts 才使用的,如果不使用可以直接省略。
    • 将项目中的 mian.js 重命名为 main.ts,并且将项目中引用该文件的位置 index.html 也同步更新扩展名。
  • 使用路由 Vue Router

    • 新建文件: router/index.ts 用于配置路由信息

      import { createRouter, createWebHistory } from 'vue-router';
      const routes = [
        {
          path: '/',
          name: 'Login',
          component: () => import("/@/views/Login.vue"),
        }, {
          path: '/Home',
          component: () => import('/@/views/Home.vue'),
          children: [
            {
              path: '/index',
              component: () => import('/@/views/Index.vue'),
              meta: { title: '首页' },
            },
            {
              path: '/admin',
              component: () => import('/@/views/Admin.vue'),
              meta: { title: '状态管理' },
            },
            {
              path: '/center',
              component: () => import('/@/views/Center.vue'),
              meta: { title: '个人中心' },
            }
          ]
        }
      ]
      
      export default createRouter({
        history: createWebHistory(),
        routes,
      })
      
    • 需要在 main.ts 中注入该路由信息。

    • 可以在 .vue 页面中使用 <router-view><router-link> 等来使用路由。

  • 使用状态管理 Vuex

    Vuex 实例对象属性 主要有5个核心属性:statemutationsgettersactionsmodules。具体使用就不再赘述了,使用方法大家跟 Vue2 的一样。

    • 新建文件: store/index.ts 用于配置 vuex。其中statemutations 是一定要定义的,其他的三个属性对象根据实际需要。

      import { createStore } from 'vuex'
      export default createStore({
          state: {
              count: 0,
          },
          getters: {},
          mutations: {
              add(state) {
                  state.count++;
              }
          },
          actions: {
              add(ctx) {
                  ctx.commit('add')
              }
          },
          modules: {}
      })
      
    • 需要在项目的 main.ts 中将 Vuex 注册到全局实例中

    • 在文件中使用:通过 this.$store.state.xxx 来获取状态的值,可以直接使用状态,也可以在vue计算属性 computed 中使用。在页面的具体使用见 Admin.vue 内容。

  • 添加新页面 Views

    • Login.vue 登录页面

      <template>
        <div class="login-wrap">
          <div class="ms-login">
            <div class="ms-title">后台管理系统</div>
            <div class="ms-content">
              <a-form layout="inline" :model="formInline" @submit="handleSubmit">
                <a-form-item>
                  <a-input v-model:value="formInline.user" placeholder="Username">
                    <template #prefix
                      ><UserOutlined style="color: rgba(0, 0, 0, 0.25)"
                    /></template>
                  </a-input>
                </a-form-item>
                <a-form-item>
                  <a-input
                    v-model:value="formInline.password"
                    type="password"
                    placeholder="Password"
                  >
                    <template #prefix
                      ><LockOutlined style="color: rgba(0, 0, 0, 0.25)"
                    /></template>
                  </a-input>
                </a-form-item>
                <a-form-item>
                  <a-button
                    type="primary"
                    html-type="submit"
                    :disabled="formInline.user === '' || formInline.password === ''"
                  >
                    Log in
                  </a-button>
                </a-form-item>
              </a-form>
            </div>
          </div>
        </div>
      </template>
      
      <script>
      import { UserOutlined, LockOutlined } from "@ant-design/icons-vue";
      import { reactive, getCurrentInstance } from "vue";
      import { message } from "ant-design-vue";
      
      export default {
        setup() {
          const formInline = reactive({
            username: "",
            password: "",
          });
      
          const { ctx } = getCurrentInstance();
      
          const handleSubmit = () => {
            message.info("登录成功!");
      
            setTimeout(() => {
              ctx.$router.push("/index");
            }, 1000);
          };
      
          return {
            formInline,
            handleSubmit,
          };
        },
        components: {
          UserOutlined,
          LockOutlined,
        },
      };
      </script>
      <style scoped>
      // .... 具体代码见 Github
      </style >
      
    • Home.vue 后台页面

      <template>
        <a-layout id="components-layout-demo-custom-trigger">
          <a-layout-sider v-model:collapsed="collapsed" :trigger="null" collapsible>
            <div class="logo">
              <span>后台管理中心</span>
            </div>
            <a-menu theme="dark" mode="inline" v-model:selectedKeys="selectedKeys">
              <a-menu-item key="1">
                <router-link :to="{ path: '/index' }">首页</router-link>
              </a-menu-item>
              <a-menu-item key="2">
                <router-link :to="{ path: '/admin' }">状态管理Vuex</router-link>
              </a-menu-item>
              <a-menu-item key="3">
                <router-link :to="{ path: '/center' }">个人中心</router-link>
              </a-menu-item>
            </a-menu>
          </a-layout-sider>
          <a-layout>
            <a-layout-header style="background: #fff; padding: 0">
              <menu-unfold-outlined
                v-if="collapsed"
                class="trigger"
                @click="() => (collapsed = !collapsed)"
              />
              <menu-fold-outlined
                v-else
                class="trigger"
                @click="() => (collapsed = !collapsed)"
              />
            </a-layout-header>
            <a-layout-content
              :style="{
                margin: '24px 16px',
                padding: '24px',
                background: '#fff',
                minHeight: '280px',
              }"
            >
              <router-view></router-view>
            </a-layout-content>
          </a-layout>
        </a-layout>
      </template>
      
      <script>
      import {
        UserOutlined,
        VideoCameraOutlined,
        UploadOutlined,
        MenuUnfoldOutlined,
        MenuFoldOutlined,
      } from "@ant-design/icons-vue";
      
      export default {
        components: {
          UserOutlined,
          VideoCameraOutlined,
          UploadOutlined,
          MenuUnfoldOutlined,
          MenuFoldOutlined,
        },
        data() {
          return {
            selectedKeys: ["1"],
            collapsed: false,
          };
        },
      };
      </script>
      <style scoped>
      // .... 具体代码见 Github
      </style >
      
    • Index.vue 后台首页

      <template>
        <img class="img" src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2685630714,1375929910&fm=26&gp=0.jpg" alt="" srcset="">
      </template>
      
    • Admin.vue 状态管理页面Vuex

      <template>
        <h2>通过 Vuex 实现状态管理</h2>
        <h3>state from vuex: {{ count }}</h3>
        <br />
        <a-button type="primary" @click="add">add</a-button>
      </template>
      
      <script>
      import { computed, getCurrentInstance } from "vue";
      export default {
        setup() {
          const { ctx } = getCurrentInstance();
          const count = computed(() => ctx.$store.state.count);
          const add = () => {
            ctx.$store.commit("add");
          };
          return {
            count,
            add,
          };
        },
      };
      </script>
      
  • 根据实际更新 main.ts

    import { createApp } from 'vue'
    import App from './App.vue'
    import router from './router'
    import store from './store'
    import './styles/index.scss'
    import Antd from 'ant-design-vue';
    import 'ant-design-vue/dist/antd.css';
    
    const app = createApp(App)
    app.mount('#app')
    app.use(Antd)
    app.use(router)
    app.use(store)
    
    • 此处定义路由时可能会报找不到模块“xxx”或其相应的类型声明。ts(2307) 的错误,该解决方案请移至文章末尾查看。
  • 更新 App.vue

    <template>
      <div id="app">  
        <router-view></router-view>
      </div>
    </template>
    <script></script>
    <style>
      @import "./styles/index.scss";
    </style>
    

完整代码请移步至 GitHub 下载。


问题解决方案

1. 找不到模块“xxx”或其相应的类型声明。ts(2307)

  • 问题:在用 vue3 中使用 ts时,在 .ts 文件中引入 .vue 文件时出现以下报错:
    在这里插入图片描述

  • 原因:typescript 只能理解 .ts 文件,无法理解 .vue 文件

  • 解决方案:在项目根目录或 src 文件夹下创建一个后缀为 .d.tsshims-vue.d.ts 文件,告诉 TS 如何理解 .vue 文件,并写入以下内容:

    declare module '*.vue' {
        import { ComponentOptions } from 'vue'
        const componentOptions: ComponentOptions
        export default componentOptions
    }
    

    在次查看之前报错的地方,错误消失,问题解决。

    如果写了这个还是报错的话,新建一个 tsconfig.json 文件,也可以解决不需要总是打开 shims-vue.d.ts 来解决 TS 报错的问题

    {
      "compilerOptions": {
        "target": "esnext",
        "module": "esnext",
        "strict": false,
        "jsx": "preserve",
        "moduleResolution": "node"
      }
    }
    

至此,一个基本完整的小项目搭建完成,大家可以根据自己的需求进行项目的实际开发。


Vite2.0 已经发布了,如果想看一下具体内容,请参考:vite1.0还没学呢 这就出2.0了?

Logo

前往低代码交流专区

更多推荐