服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

一个服务端同学的Vue框架入门及实践

日期: 来源:大淘宝技术收集编辑:赵计刚(至缺)




做为服务端同学,接触前端代码较少,刚毕业的时候用过 jQuery + Bootstrap2/3,当时的感觉就是,容易上手,学习门槛相对较低,另外就是有一个非常成熟的 jQuery 插件库,在这里,几乎可以找到日常需要的大部分插件(这里的插件,不是简单的按钮/表单组件,而是像抽奖/大转盘/推箱子游戏这样的完整的功能块,这些避免了自己去写“困难”的 CSS),组合组合就可以搭出一个完善的页面,改改 Ajax 请求,就能实现一个小项目了。

前端技术日新月异,用 jQuery 的日渐变少,现在有两个流行的技术框架 Vue 和 React。个人选择学习了解相对简单的 Vue,目的是可以独立做一个简单的管理系统,后续可沉淀成个人顺手的一个管理系统的脚手架。

 jQuery插件库地址:https://www.jq22.com/

项目效果


用户首先到登录页,登录后,进入系统首页,点击左侧菜单,可以切换界面,包括一个书籍的增删改查操作。


技术栈


后端:SpringBoot

前端:Vue + TypeScript(简称 ts) + Vite + VueRouter + Axios + ElementPlus + Pinia


所选技术

版本号

技术定位

对比技术

差异点

Vue

3.2.45

渐进式的 js 框架,支持响应式

Vue2

性能优/包体积更小/对 ts 的支持好/漂亮的语法糖

React

简单易学,详细对比

TypeScript

4.7.4

基于 js 的可强类型的编程语言

js

ts 是 js 的超集,ts 面向对象,可强类型,支持泛型、接口、类等,熟悉 Java 的同学很容易上手

ts 代码可转换为 js 代码,然后在浏览器进行执行

Vite

4.0.0

工具链:实现构建/开发服务器等功能

webpack

速度快、Vue3 推荐使用

Pinia

2.0.28

状态管理器

Vuex

Vue3 推荐,Pinia 可看做是新版的 Vuex,具体见 官网描述

VueRouter

4.1.6

页面路由器



Axios

1.2.2

调用后端服务的客户端工具



ElementPlus

2.2.28

基于 Vue3 的组件库




Vue与React详细对比:https://www.mindk.com/blog/react-vs-vue/

Pinia与Vuex关系描述:https://vuex.vuejs.org/


搭建开发环境


本人使用 mac 版进行介绍

  安装 Node.js 和 npm


在官网下载 Node.js,一路点击安装,会安装 Node.js 和 npm。最后显示如下信息,成功。


官网地址:https://nodejs.org/en


Node.js v18.12.1 to /usr/local/bin/nodenpm v8.19.2 to /usr/local/bin/npm


安装完毕之后,在控制台输入 node -v,返回版本号。
配置 npm 数据源(加速 npm install)

// 查看当前数据源npm config get registry // 默认是https://registry.npmjs.org/// 设置 npm 数据源为淘宝数据源npm config set registry https://registry.npm.taobao.org

  安装开发工具 VisualStudioCode


在 官网下载 VisualStudioCode(简称 vsCode),解压安装。之后安装 Vue 插件 vol‍ar。


官网地址:https://code.visualstudio.com/
volar 地址:https://marketplace.visualstudio.com/items?itemName=Vue.volar

  使用脚手架初始化代码


Vue 有两个脚手架:一个基于 Vite,一个基于 Vue CLI(基于 webpack),后者已经处于维护状态,新项目官方建议使用 Vite。


输入如下命令使用 Vite 来初始化一个项目。


npm init vue@latest //初次执行该命令,会安装和执行 create-vue,它是 Vue 提供的官方脚手架工具


之后,按照提示,输入项目名称,选择需要的组件。


✔ Project name: … ${projectName} // 项目名称✔ Add TypeScript? … Yes // 选择 ts✔ Add Vue Router for Single Page Application development? … Yes // 增加 VueRouter✔ Add Pinia for state management? … Yes // 增加 Pinia
Scaffolding project in /Users/jigang/Desktop/vue-study/vue-boot...Done. Now run:// 项目初始化之后,执行如下命令,可以安装相关的依赖,启动项目 cd vue-boot npm install npm run dev


执行 npm run dev,之后看到如下信息,表示服务启动成功,浏览器访问 http://localhost:5177/ 即可看到界面。


  VITE v4.0.4  ready in 306 ms  ➜  Local:   http://localhost:5177/  ➜  Network: use --host to expose  ➜  press h to show help


到这里我们整个开发环境(包括脚手架生成的项目结构)就准备就绪了,后续将脚手架生成的项目引入 vsCode 就可以进入开发了。


代码结构


如果是小型项目,用单一项目(前后端放一起)进行开发就好,这里使用常见的前后端分离的方式。

前端代码:https://github.com/zhaojigang/vue-boot(单页面应用)

后端代码:https://github.com/zhaojigang/vue-springboot


后端服务提供了简单的服务接口,通过访问 swagger 展示如下:


下面介绍前端代码。
基于上述的脚手架 + 编码最终形成的目录结构如下:


├── index.html  界面入口(定义根div)├── public      公共静态资源包│   └── favicon.ico├── src│   ├── main.ts 入口ts文件(创建应用/use各种插件/挂载到index.html的根div上)│   ├── App.vue 根组件│   ├── assets  静态资源包       │   │   └── main.css│   ├── layout 布局组件│   │   ├── Menu.vue   菜单组件│   │   └── index.vue  基础布局组件(header/aside/footer等)│   ├── views 业务视图组件(路由跳转页面)│   │   ├── book│   │   │   └── BookListView.vue book视图组件│   │   ├── home│   │   │   └── HomeView.vue     首页视图组件│   │   └── login│   │       └── LoginView.vue    登录页视图组件│   ├── components 业务组件│   │   └── book   book组件│   │       └── AddBookDialog.vue 新增书籍弹窗组件│   ├── router 路由│   │   ├── index.ts       业务路由│   │   └── permission.ts  路由守卫│   ├── stores 状态存储器│   │   └── user 业务状态存储│   │       └── user.ts  user状态存储(state/getter/actions)│   ├── types 接口定义(制定接口标准)│   │   ├── book.ts book相关接口│   │   └── user.ts user相关接口│   ├── utils 工具│   │   ├── constants.ts 常量│   │   └── login.ts     登录/退出接口封装│   └── api     axios请求封装api│       ├── baseRequest.ts axios基本封装(axios对象创建/拦截器)│       ├── book.ts        book业务请求api│       └── login.ts       login业务请求api├── .env.development 开发环境配置文件├── .env.production 生产环境配置文件├── package.json└── vite.config.ts


‍‍‍‍‍‍‍‍‍

下面分类别来看下各个‍‍文件的核心码。


  框架文件


三个核心框架文件 index.html / main.ts / App.vue


  • 界面入口


index.html:整个单页面应用入口,提供容器入口。


<html lang="en">  ...  <!-- 根容器 -->  <div id="app"></div>  <!-- 引入 ts 入口文件 -->  <script type="module" src="/src/main.ts"></script>  ...</html>


  • 入口 ts 文件


main.ts:基于根组件创建应用/use各种插件/挂载应用到index.html的根div上


/* 引入根组件创建函数 */import { createApp } from 'vue'/* 引入状态管理器 Pinia 创建函数 */import { createPinia } from 'pinia'/* 引入根组件 */import App from './App.vue'/* 引入 VueRouter */import router from './router'/* 引入主 css */import './assets/main.css'/* 引入路由守卫 */import '@/router/permission'
/* 基于根组件创建应用 */const app = createApp(App)/* 使用 Pinia 做状态管理 */app.use(createPinia())/* 使用 VueRouter 做路由 */app.use(router)/* 应用管理 index.html 中的 id=app 的 div */app.mount('#app')


  • 根组件


App.vue:Vue 单页面应用推畅组件化编程,App.vue 是所有组件的父组件,被称为根组件,main.ts 基于该根组件创建应用


<!-- 根组件 --><!-- 使用 setup 语法糖,使用 TypeScript --><script setup lang="ts">/* 引入router函数 */import { RouterView } from 'vue-router'</script>
<template> <!-- 外层路由到的页面会在此处渲染 --> <RouterView></RouterView></template>


  界面/组件划分


  • 引入 ElementPlus


1. 安装npm install element-plus --save
2. 使用按需引入方式,安装如下插件npm install -D unplugin-vue-components unplugin-auto-import
3. 配置 vite.config.tsimport { defineConfig } from 'vite'// 增加如下配置import AutoImport from 'unplugin-auto-import/vite'import Components from 'unplugin-vue-components/vite'import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({ // ... plugins: [ // ... AutoImport({ resolvers: [ElementPlusResolver()], }), Components({ resolvers: [ElementPlusResolver()], }), ],})
4. 使用组件在组件库(https://element-plus.gitee.io/zh-CN/component/button.html)寻找相应的组件,引入相关代


  • 界面/组件划分


如“项目效果”部分所见,一共三个界面:LoginView(登录界面)/HomeView(首页界面)/BookListView(书籍管理界面)。


其中 HomeView 和 BookListView 界面需要展示在一个具有 header(页头) / aside(菜单) / main(主展示区)的布局(layout)中。


图片来自 element-plus container 布局容器


各个业务界面中需要抽取成的组件放置在 components 业务组件包下(eg. 新增书籍弹窗组件),个人会将这两类特征的功能抽取成组件:

  1. 高内聚原则:功能具有一定的复杂度和隔离度,将这些代码内聚起来进行开发,不腐化外层组件。

  2. 可复用原则:功能被其他多个组件引入复用,则需要将这些代码形成组件,避免重复代码散落,降低维护成本。


所以最终形成了如下的界面布局相关的代码包结构。


└── src    ├── layout 布局组件    │   ├── Menu.vue   菜单组件    │   └── index.vue  基础布局组件(header/aside/footer等)    ├── views 业务视图组件(路由跳转页面)    │   ├── book    │   │   └── BookListView.vue book视图组件    │   ├── home    │   │   └── HomeView.vue     首页视图组件    │   └── login    │       └── LoginView.vue    登录页视图组件    └── components 业务组件        └── book   book组件            └── AddBookDialog.vue 新增书籍弹窗组件

当访问登录链接的时候,LoginView.vue 界面组件展示在 App.vue 的 <RouterView /> 处;而当访问首页或者书籍管理链接的时候,相应的 HomeView.vue 和 BookListView.vue 需要展示在布局文件 layout/index.vue 的 main区(该区也会配置 <RouterView />),layout 的 aside 部分的菜单需要封装为单独的 Menu.vue 组件。以上的访问路径到界面的映射以及当项目中具有多个 <RouterView/> 时,怎么定位到合适的 <RouterView/> 组件展示区,可见“路由配置”部分。

来看下 layout/index.vue。界面使用 ElementPlus 的页面布局容器组件


<template>    <el-container class="layout-container">        <el-header>            <div class="toolbar">                <el-dropdown>                    <el-icon>                        <setting />                    </el-icon>                    <template #dropdown>                        <el-dropdown-menu>                            <el-dropdown-item @click="logout">退出</el-dropdown-item>                        </el-dropdown-menu>                    </template>                </el-dropdown>                <span>张三</span>            </div>        </el-header>        <el-container>            <el-aside width="200px">                <el-scrollbar>                    <!-- 菜单组件 -->                    <Menu />                </el-scrollbar>            </el-aside>            <el-main>                <!-- main 界面展示区 -->                <RouterView />            </el-main>        </el-container>    </el-container>    <!-- <Menu /> --></template>
<!-- 使用 setup 语法糖,使用typescript --><script setup lang="ts">/* 引入router函数 */import { RouterView } from 'vue-router'/* 引入子组件 */import Menu from '@/layout/Menu.vue'import { Setting } from '@element-plus/icons-vue'import { logout as userLogout } from '@/utils/login'import { useUserStore } from '@/stores/user/user'
function logout() { const token = useUserStore().getToken(); if (token) { userLogout(token) }}</script>
<style scoped>... 布局组件相关 css</style>


  • 组件间通信


组件进行拆分后,组件间需要进行数据通信。比如,书籍管理和新增书籍弹框组件就需要进行如下的通信。


通信的数据有两种:一种是属性;一种是方法。
对于属性,例如行数据的传递:
在“新增弹窗组件”(承担新增和编辑功能)中进行传递属性的引入并使用:

<script setup lang="ts">/* 引入属性 */const props = defineProps<{    bookValue: Book | undefined}>()
/* 监听父组件传递数据,渲染表单 */watch(form.value, () => { if (props.bookValue) { form.value = props.bookValue } }, { deep: true, immediate: true })</script>


在“书籍管理组件”中进行属性定义赋值并通过引入组件进行传递。


<template>    <!-- 传递属性 -->    <AddBookDialog ... :bookValue="bookValue" /></template>
<script setup lang="ts">/* 定义属性并赋值 */ const bookValue = ref<Book>()// 新增或者更新数据function addOrUpdateBook(book: Book | undefined) { if (!book) { bookValue.value = undefined } else { bookValue.value = book }}</script>

对于方法,例如获取书籍列表方法的传递:
在“新增弹窗组件”中进行传递方法的引入并调用:

<script setup lang="ts">/* 引入方法 */const emits = defineEmits(['getBooks'])
/* 新增或者编辑数据成功之后,刷新列表 */const addOrUpdateBookInner = async () => { await addOrUpdate(form.value) ... /* 使用方法 */ emits('getBooks')}</script>


在“书籍管理组件”中进行方法定义并通过引入组件进行传递。


<template>    <!-- 传递方法 -->    <AddBookDialog ... @getBooks="getBooks" /></template>
<script setup lang="ts">/* 定义方法 */const getBooks = async () => { await getByRequest(queryParams.value).then(resp => { tableData.value = resp.data.dataList totalCount.value = resp.data.totalCount })}</script>

▐  路由配置


  • 基本原理


如果没有在生成脚手架的时候选择 VueRouter,需要先按照官网进行安装。

main.ts 引入 VueRouter,之后  => 通过 <RouterLink to="${path}"> 配置指定元素的跳转路径 path  => 访问中心路由 ts 文件,根据 path 找到相关的组件以及 <RouterView> 区  => 将组件展示在相应的 <RouterView> 区

整个路由的使用需要在 main.ts 引入路由器


/* 引入 VueRouter */import router from './router'/* 使用 VueRouter 做路由 */app.use(router)


菜单组件 layout/Menu.vue(引入 ElementPlus 的菜单组件,菜单路径配置有简化写法,以下使用简化写法)


<!-- 菜单组件 --><template>    <el-menu default-active="1" class="el-menu-vertical-demo" router>        <!-- index 指定跳转路径 -->        <el-menu-item index="/">            <el-icon><icon-menu /></el-icon>            <span>首页</span>        </el-menu-item>        <el-menu-item index="/bookList">            <el-icon><document /></el-icon>            <span>书籍管理</span>        </el-menu-item>    </el-menu></template>
<script lang="ts" setup>import { Document, Menu as IconMenu } from '@element-plus/icons-vue'import { RouterLink } from 'vue-router'</script>


复杂写法:


        <RouterLink to="/bookList">            <el-menu-item index="/bookList">                <el-icon><document /></el-icon>                <span>书籍管理</span>            </el-menu-item>        </RouterLink>

页面路由 router/index.ts

import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({ history: createWebHashHistory(import.meta.env.BASE_URL), routes: [ /* 登录页 path => LoginView.vue 展示在根组件的 <RouterView> 区 */ { path: '/login', name: 'login', component: () => import('@/views/login/LoginView.vue') }, /* 其他路由 */ { path: '/', name: 'layout', /* 嵌套路由,当访问 children 中的 path 时,相关的组件展示在父组件(此处是 layout/index.vue)的 <RouterView> 区 */ component: () => import('@/layout/index.vue'), children: [{ path: '/', name: 'home', component: () => import('@/views/home/HomeView.vue'), }, { path: '/bookList', name: 'bookList', component: () => import('@/views/book/BookListView.vue') } ] } ]})
export default router

展示区 layout/index.vue


<template>    <el-container class="layout-container">        ...        <el-main>            <!-- 路由展示区 -->            <RouterView />        </el-main>        ...    </el-container></template>


  • 路由守卫


作用:根据是否登录来控制用户对页面的访问,如果已登录,可进行目标页面访问;如果未登录,跳转到登录页


import router from "./index";import { useUserStore } from '@/stores/user/user'
/* 白名单 */const whiteList = ['/login']/* userStore */// to - 要去的导航// from - 当前的导航// next - 函数,可指定去往任意的导航router.beforeEach((to, from, next) => { const userStore = useUserStore() if(userStore.getToken()) { /* 如果 token 存在 */ if(to.path === '/login') { /* 跳转到首页 */ next('/') } else { /* 继续跳转到 to */ next() } } else { /* 如果 token 不存在 */ if(whiteList.includes(to.path)) { next() } else { next('/login') } }})


编写路由守卫后,在 main.ts 中引入就可生效。


/* main.ts 引入路由守卫 */import '@/router/permission'


  状态存储


当一个数据需要在多个组件进行使用时,就可以考虑使用 Pinia 实现状态存储。如果没有在生成脚手架的时候选择 Pinia,需要先按照官网进行安装。

Pinia 的使用需要在 main.ts 进行引入

/* 引入状态管理器Pinia创建函数 */import { createPinia } from 'pinia'/* 使用 pinia 做状态管理 */app.use(createPinia())

在项目中会有多个组件使用用户登录信息,可以将该数据存储在 userStore 中。


首先定义业务存储,store/user/user.ts‍


import { defineStore } from 'pinia'import { TOKEN_DURATION_KEY, TOKEN_DURATION } from '@/utils/constants'// 命名规范:建议使用 useXxxStoreexport const useUserStore = defineStore('user', () => {    // 在 Setup Store 中:    // ref() 就是 state 属性    // computed() 就是 getters    // function() 就是 actions    function saveToken(token: string) {        localStorage.setItem('token_vue_springboot', token)    }
function getToken(): string | null { return localStorage.getItem('token_vue_springboot') }
function removeToken() { localStorage.removeItem('token_vue_springboot') }
/* 登录时设置 */ function saveCurrentTokenTime() { localStorage.setItem(TOKEN_DURATION_KEY, Date.now().toString()) }
function getTokenTime(): number | null { const tokenTime = localStorage.getItem(TOKEN_DURATION_KEY) if (tokenTime) { return Number(tokenTime) } return null }
function removeTokenTime() { localStorage.removeItem(TOKEN_DURATION_KEY) }
/* 判断登录 token 时间是否过期 */ function tokenTimeIsExpire(): boolean { const tokenTime = getTokenTime(); /* 如果不存在,则表示未登录,与过期等价,需要重新登录 */ if (!tokenTime) { return true } return Date.now() - tokenTime > TOKEN_DURATION }
/* 暴露方法 */ return { saveToken, getToken, removeToken, saveCurrentTokenTime, getTokenTime, removeTokenTime, tokenTimeIsExpire }})

然后,使用 useUserStore 状态存储器

import { login as userLogin } from '@/api/login'import { useUserStore } from '@/stores/user/user'import router from '@/router'import type { Ref } from 'vue'
export function login(loginForm: Ref<LoginUser>) { /* 使用 Pinia Store */ const userStore = useUserStore() /* 定义函数 */ async function loginTrue() { await userLogin(loginForm.value).then(resp => { /* 保存 token 到浏览器缓存 */ userStore.saveToken(resp.data.token) /* 保存当前时间 tokenTime 到浏览器缓存 */ userStore.saveCurrentTokenTime() /* 跳转到首页 */ router.replace('/') }) } /* 调用登录函数 */ loginTrue()}


  服务请求封装


请求后端服务使用 Axios,需要进行安装


  • Axios 客户端封装


api/baseRequest.ts


/* 封装 axios 对象 */import axios from "axios";import { useUserStore } from '@/stores/user/user'import { logout } from '@/utils/login'
/* 创建请求对象,填入全局配置参数 */const axiosService = axios.create({ baseURL: import.meta.env.VITE_DOMAIN_URL_VUE_BOOT, // 根据环境读取不同的文件,具体见“环境配置”部分 timeout: 3000})
/* 请求前拦截器 */axiosService.interceptors.request.use(function (config) { // 在发送请求之前做些什么,eg.token校验;token传递到header,进行免登操作等 /* 如果 token 过期 */ const userStore = useUserStore() if (userStore.tokenTimeIsExpire()) { logout() return Promise.reject(new Error('token expired')) } return config;}, function (error) { // 对请求错误做些什么 return Promise.reject(error);})
/* 响应拦截器,拦截之后,业务方法基于该返回进行数据处理(此处可进行服务端返回数据的统一处理,例如,统一报错等) */axiosService.interceptors.response.use(function (response) { // 2xx 范围内的状态码都会触发该函数。 // return response.data return response}, function (error) { // 超出 2xx 范围的状态码都会触发该函数。 return Promise.reject(error);})
export default axiosService


  • 业务 api 封装


为了代码比较好治理,可以将不同的业务对服务端的调用封装为不同的 ts 文件,eg. login.ts 封装登录相关的调用


/* 封装 login 相关api */import axiosService from './baseRequest'// 登录export function login(data: {}) {    return axiosService.request({        url: 'user/login',        method: 'post',        data: data    })}// 退出export function logout(data: {}) {    return axiosService.request({        url: 'user/logout',        method: 'post',        data    })}


  接口定义标准


ts 提供了接口能力,可用于实现编写代码的时候,就出现一些提示和错误标识;同时可以定义一些标准调用。

由于接口定义可能会在多个文件中进行调用,同时为了方便管理,建议接口写在 types 目录下的相关业务文件中。

Book 接口定义:types/book.ts

// 定义Book接口export interface Book {    id: number,    name: string}
// 定义查询请求参数接口export interface BookRequest { /* 模糊查询 */ name: string, pageNum: number, pageSize: number}

使用接口:BookListView.vue


// 导入外部接口定义import type { Book,BookRequest } from '@/types/book'
// 构造列表查询数据const queryParams = ref<BookRequest>({ name: "", pageNum: 1, pageSize: 2})
// 获取列表数据const totalCount = ref(0)const tableData = ref<Book[]>([])const getBooks = async () => { await getByRequest(queryParams.value).then(resp => { tableData.value = resp.data.dataList totalCount.value = resp.data.totalCount })}getBooks()

  环境配置


环境配置相关的代码包结构。


根路径├── .env.development 开发环境配置文件├── .env.production 生产环境配置文件└── package.json

.env.development

# 开发环境配置  VITE_NAME='开发环境'VITE_DOMAIN_URL_VUE_BOOT=http://localhost:8082

.env.production


VITE_NAME='生产环境'VITE_DOMAIN_URL_VUE_BOOT=http://localhost:8082

package.json 配置命令使用的模式


{  ...  "scripts": {    "dev": "vite --mode development", // 开发启动 "npm run dev"    "build": "run-p type-check build-only", // 生产构建静态资源到 /dist 目录 "npm run build"    "preview": "vite preview", // 本地运行 /dist 目录文件,进行提前验证 "npm run preview"    "build-only": "vite build --mode production",    "type-check": "vue-tsc --noEmit"  },  ...}


使用配置文件使用环境变量 baseRequest.ts


const axiosService = axios.create({  baseURL: import.meta.env.VITE_DOMAIN_URL_VUE_BOOT,   timeout: 3000})

项目部署

本人通过以上这样的开发模式研发了多人实时玩法管理后台。开发完成后,就要去做应用部署,前后端分离项目的常规部署方式如下图所示。


前端资源(静态资源/css/js等)单独部署在CDN,服务端使用模板引擎(例如,Thymeleaf)来编写入口文件 index.html(代码与前端代码中编译后的 index.html 几乎相同,只是其中引入的 css 和 main.js 资源路径是资源的CDN 地址),这样通过服务端域名访问项目的时候,就会执行后端的 index.html,进而执行到 main.js(main.js 是 main.ts 的编译产物)。

这样的前后端分离方式是基于“index.html 代码几乎不怎么变动” 的前提下进行的,否则,后端代码需要因为这个文件的变动不断发布。而默认情况下,Vite 在构建文件的时候会生成带 hash 值的文件,例如,main.ts 被编译为 main-[hash].js,而 index.html 需要引入这个 js 文件,为了保证这个文件路径的不变性,需要在 vite.config.ts 文件中增加几行配置。

  build: {    rollupOptions: {      input: "./src/main.ts",      output: {        dir: "dist",        /* 去除hash值 */        entryFileNames: "assets/[name].js",      }    }

参考资料


  1. Vue3 后台管理系统(地址:https://www.bilibili.com/video/BV1pq4y1c7oy/)

  2. TS 快速入口 + Vue3 快速入门(Vue部分需要结合 Vue3 官方文档看。地址:https://www.bilibili.com/video/BV1ra4y1H7ih/?vd_source=480e34eaa7e8621cbd83d5f3163fc061)

  3. 技术栈部分列出的各种官网


团队介绍


我们是大淘宝技术投放平台团队,负责双11、618、造物节等天猫淘宝大促和各类营销活动业务,覆盖几亿消费者、千万商家。这里是创造新商业的前沿阵地,同时充满着各种技术挑战 - 百万级峰值qps的页面投放、亿级规模的权益发放、T级大规模的数据供给等。我们致力于营销选品、投放、权益等技术体系的建设,打造一套灵活的业务解决方案,让业务创新更高效。


相关阅读

  • 预制菜产业扶持政策文件即将发布

  • 3月24日,第七届中国国际食品及配料博览会、首届中国国际预制菜产业博览会在广东省东莞市举办。中国工程院院士、湖南省农科院院长单杨在接受中国农村网记者专访时表示,相关部
  • 外行人都能看懂的SpringCloud,错过了血亏!

  • 原价799元,现在参加拼团活动立享优惠价仅 199 元,赶快一起参团吧!《SpringCloudAlibaba源码剖析专家课》=== 课程内容 ===现在大部分的公司在选择微服务技术框架的使用都会用sp

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • 楼继伟:新时代中国财政体系改革和未来展望

  • 进入新时代以来的十年间,公共财政体制按照《决定》要求,巩固和发展前期改革的成果,并进行了广泛的渐进式改革,初步建立了现代财政制度。探讨财政体系的未来发展需要问题导向,并提
  • 一个服务端同学的Vue框架入门及实践

  • 做为服务端同学,接触前端代码较少,刚毕业的时候用过 jQuery + Bootstrap2/3,当时的感觉就是,容易上手,学习门槛相对较低,另外就是有一个非常成熟的 jQuery 插件库,在这里,几乎可以找
  • 周其仁:反思农村金融

  • 作 者:周其仁 来 源:人文经济课堂反思农村金融——农民收入是一连串事件之十周其仁其实,每一张钞票都是一纸文契。你看,文字书写、抽象到只用数字表达持有人的权利,以及权威
  • 黄奇帆:今后30年,中国应该抓住这些机会

  • 黄奇帆 中国发展高层论坛供图黄奇帆在中国发展高层论坛上说,今后二三十年,中国应该抓住类似像无人驾驶的新能源汽车、家政服务的人型机器人、提供数字秘书服务功能的智能终端
  • 耿兆锐:“中国热”漩涡里的法国重农学派

  • 作 者:耿兆锐 来 源:本文原载于《经济学家茶座》2015年第4期(总第70辑)17至18世纪,欧洲兴起了长达百年的“中国热”(China Vogue)。这股来自遥远东方的异国文化在浪漫之地——