【中台】靓仔请留步,跟我一起撸中后台啊~(系列篇二:项目配置及依赖 )

1,191 阅读2分钟

前言

啰嗦几句哈,系列篇一 中的 Demo 项目不会作为正式的项目,在写那个Demo项目时,Vue3 还没有出来,在 Vue2.0 里面写 TypeScript 是比较麻烦的,但是通过 vue-class-component 写起来就还不错,虽然要去熟悉好多新的东西,增加了心理压力哈哈哈哈~~~

终于啊,Vue3 出来了,天然支持 TypeScript,我们再也不用增加那么多心智负担了,废话就不说了,直接撸起袖子干。

环境依赖

- node v14.15.1 
- npm 7.7.2
- qiankun ^2.4.1
- @vue/cli 4.5.11 
- Vue3 ^3.0.0
- Typescript ~3.9.3
- ant-design-vue ^2.0.0
- vue-router ^4.0.4
- vuex 4.0

初始化项目

通过 vue create project-name 创建项目

选择 Manually select features

选择 TypeScript,选择 3.x (Preview)

在初始化项目的时候,最好是安装 vue-routervuex,因为这也是我们项目中必须要的依赖,否则我们在写的过程中还需要手动去添加!

选择 ESLint + Standard configLint on save

选择In dedicated config files,在专用配置文件中配置各自的信息

选择 Save this as a preset for future projects? Yes,将配置保存为未来项目的预设,将来创建项目就可以直接选择你的模板了。

最后回车,静静的等待就好了。

具体配置如图所示:

image.png

项目初始化完成之后,打开项目看到如下目录结构:

image.png

然后我们随便改点东西试试:

image.png

到此我们的 Vue3 + TypeScript 项目就创建好了。

ant-design-vue 2.0

安装

npm install ant-design-vue@^2.0.0 -S

yarn add ant-design-vue@^2.0.0 -S

按需引入

安装 babel-plugin-import 插件

修改 babel.config.js

module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  plugins: [
    [
      "import",
      { libraryName: 'ant-design-vue', libraryDirectory: "es", style: true }
    ]
  ]
}

如何使用this.$message

  • 方法一: 使用 provide/inject(用于组合或选项API)
import { message } from 'ant-design-vue'

const app = createApp({ ... })

app.provide('message', message)

在组件中使用

import { inject } from 'vue'

setup() {
  const $message = inject('message')
  
  $message.success('Hello_AlexCc')
}
  • 方法二: 使用全局配置 globalProperties

注意:这只适用于选项API。

import { message } from 'ant-design-vue'

const app = createApp({ ... })

app.config.globalProperties.$message = message

在组件中使用

mounted() {
  this.$message.success('mounted: this.$message.success')
}

或者

<a-button @click="$message.error('error!!')">弹窗</a-button>

使用ant图标

当你复制一个图标粘贴到代码里去,它是一个组件,比如 问号 图标 <QuestionOutlined />

你需要从 @ant-design/icons-vue 中导入。

如果没有安装的话,需要 yarn add @ant-design/icons-vue

使用 iconfont 图标

iconfont官网

首先我们去到 iconfont 官网,找一些你喜欢的图标,将他们收藏到你自己的仓库。

import { StepForwardOutlined, createFromIconfontCN } from '@ant-design/icons-vue'

// 使用 iconfont 图标,这里的 url 就是你收藏的 iconfont 项目 js 路径
const IconFont = createFromIconfontCN({
  scriptUrl: '//at.alicdn.com/t/font_2299454_fvw4ct8o61.js'
})

// 局部注册 IconFont
components: { IconFont }

// 全局注册
export default {
  // App 是 vue实例 的类型
  install: (app: App, options: InstallOptions = defaultInstallOpt ) => {
    app.component('icon-font', IconFont)
    AntdComponent.forEach(comp => {
      app.component(comp.name, comp)
    })
  }
}

使用图标

<div class="icon-list">
  <icon-font type="icon-mugua"></icon-font>
  <icon-font type="icon-caomei"></icon-font>
  <icon-font type="icon-xigua"></icon-font>
  <icon-font type="icon-yingtao"></icon-font>
  <icon-font type="icon-douya"></icon-font>
  <icon-font type="icon-xianggu"></icon-font>
  <icon-font type="icon-angel"></icon-font>
</div>

<style scoped lang="less">
.icon-list {
  padding: 2em 0;
  :deep(.anticon) {
    font-size: 36px;
    margin-right: .5em;
  }
}
</style>

效果如下。

image.png

vue-router 4

安装

使用npm 或者 yarn

npm install vue-router@4 -S

yarn add vue-router@4 -S

使用

createRouter 函数可以创建一个被 Vue 应用程序使用的实例。Options 用来初始化 router

createWebHistory 创建一个HTML5历史,单页面应用程序中最常见的历史记录。也就是之前版本的 mode : history。 另一种常见的 hash 模式,在 v4 中采用 createWebHashHistory。路由表中的用法还是跟以前一样用就行了。

// router/index.ts
export default createRouter({
  history: createWebHistory(),
  routes: constantRoutes
})

不过,在 composition api 中我们都是通过 setup 钩子作为组合式API的入口,里面是无法使用 this 的,不过在 template 中可以使用 $router 或者 $route

那么我们如何像之前那样通过编程式路由 this.$router 或者 this.$route 呢?

使用 useRouteruesRoute

import { useRouter, useroute } from 'vue-router'

setup() {
  const router = useRouter()
  const store = useStore()

  router.push('/login')
}

其它 Router Api 去官网查看就好了。

Vuex 4.0

安装

使用 npm 或者 yarn 安装

npm install vuex@next --save

yarn add vuex@next --save

使用

创建 store/index.ts 文件,从 vuex 中导入 createStore 来创建我们的 store 实例,写好内容后不要忘了使用 vue 去注册。

// store/index.ts

import { createStore } from "vuex"

// 定义存储状态的类型
export interface State {
  count: number
}

// 如果不将 'State' 接口传入到 'createStore' 中,在 'mutations' 中使用的 state 会被推测为 'unknown' 类型
const store = createStore<State>({
  state() {
    return {
      count: 0
    }
  },
  mutations: {
    increment(state) {
      state.count++
    }
  }
})

export default store

注册store

// main.ts

const app = createApp({ /* your root component */ })
app.use(store)

当我们需要用 在 store 中定义的 count 时,发现 count 的类型是 any

image.png

当我们在Composition API中编写Vue组件时,很可能希望useStore返回类型化的存储。为了让useStore正确地返回输入的存储,必须做如下几步操作:

1. 定义类型化的`InjectionKey`2.`Vue`应用程序中安装 `store` 时提供类型化的`InjectionKey`3. 将类型化的`InjectionKey`传递给`useStore`方法。

回到 store/index.ts 文件

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

// 定义注入 key
export const key: InjectionKey<Store<State>> = Symbol()

然后我们可以将key传递给useStore方法以检索类型化的存储,在使用的地方就能正确推到出state.count 的类型了。

image.png

qiankun

系列一 文中已经介绍 qiankun微前端的概念了,这里就不再啰嗦了。

安装

yarn add qiankun@2.4.1 -S

使用

虽然 系列篇一 中已经写过用法了,但是在正式开始的这个项目里面还是有点不一样,所以这里我们再看看如何再 Vue3 中使用它。

现在我们还是以一个小栗子为入口,去了解如何使用它。

首先我们在 src 下面新建一个文件 single-spa.ts 文件,用来处理连接 子应用 的配置,代码很简单,直接上代码,里面描述了一些关键信息。

//single-spa.ts

import {
  registerMicroApps,
  setDefaultMountApp,
  addGlobalUncaughtErrorHandler,
  start
} from "qiankun"

const apps = [
  {
    name: 'vue3-base', // 应用名称
    entry: '//localhost:8080', // 应用入口,资源路径
    container: '#subAppContainer', // 在主体中存放 子应用 的容器 id
    activeRule: '/vue-base', // 根据此规则切换指定子应用,激活规则
    props: {
      // 传递给子应用的数据
      user: {
        name: 'ZHONG TAI ADMIN',
        age: 18
      }
    }
  }
]

const excutorSingleSpa = () => {

  registerMicroApps(apps, {
    beforeLoad: [
      // @ts-ignore
      app => {
        console.log('加载应用之前打印apps', app)
      }
    ]
  })

  addGlobalUncaughtErrorHandler((event: Event | string) => {
    console.error(event)
    const { message: msg } = event as any
    // 加载失败时提示
    if (msg || msg.includes("died in status LOADING_SOURCE_CODE")) {
      console.error("微应用加载失败,请检查应用是否可运行", msg);
    }
  })

  start({
    // 是否开启预加载,默认为 true。
    // 配置为 true 则会在第一个微应用 mount 完成后开始预加载其他微应用的静态资源
    prefetch: false,
    sandbox: true
  })
}

export default excutorSingleSpa

在项目结构方面,我们一般都会有一个 layouts.vue 组件,里面有个一区域是用来渲染我们各个路由页面的。而我们的 子应用 也是在这里渲染的。

// layout/index.vue
// 其它结构省略

<a-layout-content
  :style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }">
  <!-- 主内容渲染部分 -->
  <router-view></router-view>
  
  <!-- 子应用渲染部分 -->
  <div id="subAppContainer"></div>
</a-layout-content>

layout/index.vue 文件中,我们引入 single-spa.ts 文件。

import useSingleSpa from  '@/single-spa'

setup() {
  useSingleSpa()
}

然后我们在 router/index.ts 中,加入 子应用 的菜单。

// routes
import { RouterView } from 'vue-router'

const routes = [
  // 省略其它路由
  {
    path: '/vue-base',
    component: Layout,
    children: [
      {
        path: 'home',
        // `RouterView` 表示显示当前路由,就跟 `<router-view />` 一样。
        // 以前我们可能是这么写 component: h => h('router-view')
        component: RouterView,
      },
      {
        path: 'about',
        component: RouterView,
      }
    ]
  }
]

到这里,主项目就差不多配置好了。然后去配置 子应用

子应用配置

如果你看了 系列篇一,就知道我们在子应用中不需要安装任何依赖,只需要在 main.ts 中导出这几个钩子函数 bootstrapmountunmount

我们看下子应用的路由如何配置。

这里的根目录 '/' 是相对于子应用的,由于通过createWebHistory('/vue-base') 设置了路由前缀,其实这里会是 /vue-base ,然后重定向到 /vue-base/about

// router/index.ts

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import Home from '../views/Home.vue'

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    redirect: '/about'
  },
  {
    path: '/home',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // 路由懒加载
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = createRouter({
  // 检测如何是由主体渲染的,就加个 `/vue-base` 路由前缀,以便区分主体和子应用的路由
  history: window.__POWERED_BY_QIANKUN__ ? createWebHistory('/vue-base') : createWebHistory(process.env.BASE_URL),
  routes
})

export default router

子应用的 App.vue 的结构如下

<template>
  <div class="app">
    <div id="nav">
      <router-link to="/home">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    
    <!-- 渲染匹配的路由component -->
    <router-view></router-view>
  </div>
</template>

然后我们创建 vue.config.js,来处理 子应用 嵌入主体项目可能会出现的问题。

const { name } = require('./package.json')

module.exports = {
  devServer: {
    headers: {
      "Access-Control-Allow-Origin": "*"
    }
  },
  configureWebpack: {
    output: {
      library: `${name}-[name]`,
      libraryTarget: 'umd',
      jsonpFunction: `webpackJsonp_${name}`
    }
  }
}

到这里,我们的子应用 vue-base 就配置好了。

我们来看下效果吧!

现在看到的界面是 主体 项目的首页。

image.png

这个界面,就是点击 导航vue-base 之后看到的页面。

image.png

到这里,我们的子应用和主体就链接起来了,之后项目不停的迭代之后,会变得越来越复杂。

一起交流

如果喜欢这部分内容,就加入我们的QQ群,和大家一起交流技术吧~

QQ群:1032965518