POM 标签大全详解

基于logstash同步mysql数据到elasticsearch

  返回  

最新Vue3+Vite+Setup开发秘籍(包含源代码)

2021/8/20 18:01:22 浏览:

前言

大家好,我是《前端发现者》,发现前端,发现技术,让前端变得更加容易。今天给大家带来一篇Vue3的开发文献,供大家在开发过程中查阅,一时看不完 收藏 点起来✌️

Vue3就在前几天尤大大发布了 V3.2.2 的版本了,此次更新在前端程序员看来可谓是皆大欢喜、欢呼雀跃。那么问题来了?

  • 旧项目要不要从Vue2升级为Vue3呢?
  • 新项目要使用Vue3吗?

答:旧项目暂且不考虑升级。新项目可以尝试用Vue3开始搞起(纯属个人意见,仅供参考!)

这个怎么说呢?旧项目需要考虑稳定,毕竟是已经在使用的产品,要是中途出现什么问题,到时怕是要删库跑路才能解决问题。新项目使用Vue3可以快速帮助我们了解新语法、新特性,实践出真知嘛。其次使用 Vite 在开发过程中是非常爽的 —— 用过都说爽的那种。最后就是面试了,现在出去找工作面试官多多少少会问一些有关Vue3的问题:一来考察你的知识储备、二来看你是不是保持一颗学习的心态,毕竟前端这块的技术更新换代太快了,最主要的是尤大大一刻不写代码就不自在啊😂😂😂

说归说,闹归闹,别拿Vue3开玩笑。基于以上的叙述,文本将给准备着手做Vue3项目的小伙伴梳理一套完整的Vue3 + Vite + setup 项目结构的开发笔记。路途长,车速慢,请坐稳扶好!

创建Vue3项目

npm创建

  • npm init vite-app <project-name>
  • cd <project-name>
  • npm install
  • npm run dev

yarn创建

  • yarn create vite-app <project-name>
  • cd <project-name>
  • yarn
  • yarn dev

我采用使用yarn创建的方式(文中提到的依赖均采用yarn安装)。yarn create vite-app course-vue3-vite。来看看初始创建的项目结构是怎么样的:

直接看看运行效果:

自带了一个点击累积的例子,一个非常适合给刚刚接触Vue3的小伙伴参考参考

至此就已经完成了初创 Vue3+Vite 项目的工作了。

vite.config.js配置

通过上面的截图可以看到,初始创建的项目是没有生成类似Vue2的vue.config.js文件的,所以需要开发者手动去创建这个文件滴。但基于Vite的构建工具呢我们需要创建的是vite.config.js文件。直接在根目录下创建,这个文件也是配置文件,自动导入使用的,不需要我们在其他文件引入这个配置文件。这里安排上我的配置文件:

import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
const path = require("path")
import styleImport from "vite-plugin-style-import"
import viteCompression from 'vite-plugin-compression'

export default defineConfig({
    publicDir: 'assets',
    build: {
        outDir: 'dist', //打包后文件的存放路径
        brotliSize:false,// => 启用/禁用brotli压缩大小报告
        //去除console
        terserOptions: {
            compress: {
                drop_console: true,
                drop_debugger:true
            }
        }
    },
    resolve: {
        alias: {
            "@": path.resolve(__dirname, "./src")
        }
    },
    server: {
        port: 3000,
        host: "10.0.0.222",
        open:true,
        proxy: {
            "/api": {
                //要访问的后端的名
                target: "http://10.0.0.177:8085",
                /** 是否启用websockets */
                ws: false,
                /** 使用的是http协议则设置为false,https协议则设置为true */
                secure: false, 
                /** 开启代理:在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样客户端端和服务端进行数据的交互就不会有跨域问题 */
                changOrigin: true,
            }
        }
    },
    plugins: [
        vue(),
        // css样式按需加载
        styleImport({
            libs: [
                {
                    libraryName: 'vant',
                    esModule: true,
                    resolveStyle: (name) => {
                      return `vant/es/${name}/style`;
                    },
                },
                {
                    libraryName: 'element-plus',
                    esModule: true,
                    ensureStyleFile: true,
                    resolveStyle: name => {
                        if (name === 'locale') return '';
                        return `element-plus/lib/theme-chalk/${name}.css`;
                    },
                    resolveComponent: name => {
                        return `element-plus/lib/${name}`;
                    }
                }
            ]
        }),
        //开启gzip或brotli来压缩资源
        viteCompression({
            // 开启gzip模式
            verbose: true,
            disable: false,
            threshold: 10240,
            algorithm: 'gzip',
            ext: '.gz'
        })
    ],
})

好吧,是稍微有点长,不过直接copy就完事啦🤔

从配置文件可以看到熟悉面孔,例如: server 选项和 resolve 选项配置,注意的是多了个 build 选项和它其下的 terserOptions/compress 选项,这是打包相关的配置和去除页面中console、debugger内容。其中styleImport和viteCompression分别是Css的按需加载配置和开启资源压缩。更多相关的配置可以参考官网:vite相关配置 [https://cn.vitejs.dev/config/#server-options]

注意:要完成上述的配置并运行成功需要安装相应的依赖,分别是:

// 打包
yarn add @vitejs/plugin-vue -D 
// 开启资源压缩
yarn add vite-plugin-compression -D
// css样式按需加载
yarn add vite-plugin-style-import -D

另外就是配置文件使用到的 defineConfig 模块,为此我们需要将Vite升级到最新版的2.x,这里我升级为2.0.0.使用,命令如下:

yarn add vite@^2.0.0

然后重新启动项目也是没有问题的。修改配置文件的 server 选项的 host 也能看到项目启动在新修改的IP上,所以我们的配置文件是生效配置的。

vue-router使用

vue是单页面程序,必然离不开页面路由的加持,而router为了兼容Vue3发布了4.x版本,这个版本跟3.x之前又有新的玩法,老规矩,先安装再上手。

yarn add vue-router@^4.0.6

这里需要指定版本,否则直接下载还是3.x的。

接着在src下创建router文件夹,并在文件夹下创建index.js文件,用来配置路由表。

vue-router创建路由实例方法是由 vue-router 导入 createRoutercreateWebHistory 两个方法

import { createRouter, createWebHistory } from "vue-router"

然后就可以利用它们创建路由实例:

export const router = createRouter({ 
  history: createWebHistory(),
  routes: constantRoutes
})

这里用了history的模式,需要使用hash模式就将createWebHistory替换为createWebHashHistory。 history官方配置 [https://next.router.vuejs.org/zh/api/#history]

倘若想引入动态路由,官方提供 addRoute 方法

router.addRoute({
    path: '/about',
    component: () => import('@/views/about/index.vue'),
    meta: {
      title: '关于'
    }
  },
  ...
)

需要提前说的是,Vue2配置遇到不存在的路由是用(*)匹配的,在Vue3就不一样了:

{
    path: '/404',
    name: '404',
    component: () => import('@/views/error-page/404.vue'),
    meta: {
      title: '404'
    }
  },
  {
    path: '/:pathMatch(.*)',
    redirect: '/404'
  },

需要使用 pathMatch 匹配不存在的路由页面跳转

看完上面,最后给出路由🌰

// index.js
import { createRouter, createWebHistory } from 'vue-router'

export const constantRoutes = [
  {
    path: '/',
    component: () => import('@/views/home/index.vue'),
    meta: {
      title: '首页'
    }
  },
  {
    path: '/login',
    component: () => import('@/views/login/index.vue'),
    meta: { 
      title: '登录'
    }
  },
  {
    path: '/404',
    name: '404',
    component: () => import('@/views/error-page/404.vue'),
    meta: {
      title: '404'
    }
  },
  {
    path: '/:pathMatch(.*)',
    redirect: '/404'
  },
]

export const router = createRouter({ 
  history: createWebHistory(),
  routes: constantRoutes
})

router.addRoute({
    path: '/about',
    component: () => import('@/views/about/index.vue'),
    meta: {
      title: '关于'
    }
  },
)
export default router

这里引入路由相信大家也看到了,我是用的是按需加载路由,这对大型项目来说打包的时候是有区别的,所以也提倡各位小伙伴配置路由时尽量选择按需加载的方式。

创建好路由必然也需要在main.js导入使用。

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './index.css'

createApp(App).use(router).mount('#app')

可以看到Vue3创建实例跟之前有所不同,使用路由也是用了链式的写法 .use()

倘若对路由的配置和使用还不是很清楚的小伙伴可以看看这篇详细的介绍。一文搞定Vue3中使用vue-router

vuex使用

首先先安装Vuex,跟Vue3适配的是4.x版本,这里安装4.0.2版本的

yarn add vuex@^4.0.2

然后跟 vue-router 思路一样,Vuex也是导入 createStore 模块去创建实例。

在src文件夹下创建store文件夹,然后在store下创建index.js文件

import { createApp } from "vue"
import { createStore } from "vuex"

//创建一个新的store实例
const store = createStore({
  state() { 
    return {
      count:345345
    }
  },
  mutations: {
    addCount(state, date) { 
      state.count+=date
    }
  },
  actions: {
    addAction({ commit },date){ 
      commit('addCount',date)
    }
  },
  modules: {},
  getters: {
    getCount(state){
      return `我是前端发现者,在测试getters方法,获取count的值是:${state.count}`
    }
  }
})
export default store

最后main.js引入使用Vuex

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

createApp(App).use(router).use(store).mount('#app')

既然用到Vuex,肯定会想到调试工具 vue-devtools,但遗憾的是Vue2版本使用的devtools在Vue3项目已不适用了,所以需要更新调试工具,这里给出下载地址。谷歌下载Vue3版的devtools [https://chrome.google.com/webstore/detail/vuejs-devtools/ljjemllljcmogpfapbkkighbhhppjdbg]

下好之后我们来看看项目看到的界面。

这里有个升级版的功能,选中Routes可以看到我们配置的路由表信息,当前页面在哪个路由:

好的,那么在组件页面中如何使用Vuex的数据?

<template>
  <div>我是vuex过来的数据:{{count}}</div>
  <div>我是vuex过来的数据2{{count2}}</div>
  <button @click="handleAddCount">我来修改Count的数据,每次加1</button>
</template>

<script>
import { computed } from "vue"
import { useStore } from 'vuex'
export default {
  setup() {
    const store = useStore()
    const count = computed(() => store.state.count)
    // 这个方法也可以获取数据
    const count2 = computed(() => store.getters.getCount) 
    function handleAddCount() {
      store.dispatch('addAction', 1)
    }
    return { count, handleAddCount, count2 }
  }
}
</script>

可以看到从vuex导入 useStore 模块然后获取Vuex实例,然后使用 computed 接收数据,这里跟Vue2一样,只不过写法上变了。

另外需要注意的是:不管是 方法 还是 变量,需要在<template>、<style>模块使用的话都需要 return{} 声明。(说实话,这个写法其实很react了,代码区块也很清晰明了,但是后面出现的setup语法糖,让写法做到了极致,不得不说,尤大大真是追求完美极致的大佬👍 )

当要修改Vuex的时候时,我们提交一个 actions,让它去代发 mutations ,然后更新数据。思路跟Vue2一样的:

<template>
  <div>我是vuex过来的数据:{{count}}</div>
  <button @click="handleAddCount">我来修改Count的数据,每次加1</button>
</template>

<script>
import { computed } from "vue"
import { useStore } from 'vuex'
export default {
  setup() {
    const store = useStore()
    const count = computed(() => store.state.count)
    function handleAddCount() {
      store.dispatch('addAction', 1)
    }
    return { count, handleAddCount }
  }
}
</script>

我这里使用点击事件模拟修改Vuex数据,点击的时候 dispatch 提交一个 actions 模块的 addAction方法,然后它内部 commit 提交事件。

对Vuex使用还不够了解的小伙伴可以看这篇文章:一文搞定Vue3中使用Vuex

axios请求封装

安装axios

yarn add axios -D

然后创建文件下utils,其下创建auth.js、axi.js、request.js三个文件。分别是处理token的逻辑、请求拦截器与请求封装、接口请求方法。一起来分别看看。

auth.js

import Cookies from 'js-cookie'
const TokenKey = 'userToken'
export function getToken() {
  return Cookies.get(TokenKey)
}
export function setToken(token) {
  return Cookies.set(TokenKey, token)
}
export function removeToken() {
  return Cookies.remove(TokenKey)
}

这里的意思是提供getToken、setToken、removeToken三个方法,用在需要token令牌的请求头、登录时设置用户信息以及退出登录时删除用户登录状态,原理就是利用 js-cookie 插件将用户登录信息存放在浏览器的cookie,所以需要安装依赖:

yarn add js-cookie -D

axi.js文件

请求拦截器

import router from '@/router/index.js'
import axios from 'axios'
import cookies from 'js-cookie'
import store from '@/store/index.js'
import { getToken } from '@/utils/auth'

axios.interceptors.request.use(
  config => {
    if (store.getters.token) {
      // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
      config.headers['X-Token'] = getToken()
    }
    if (config.headers['Content-Type'] === undefined) { 
      config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
      const data = new FormData()
      for (const key in config.data) {
        if (config.data[key] != null && config.data[key] != undefined) {
          data.append(key, config.data[key])
        }
      }
    config.data = data
    }
    return config
  },
  error => {
    Promise.reject(error)
  }
)

响应拦截器

import router from '@/router/index.js'
import axios from 'axios'
import cookies from 'js-cookie'
import store from '@/store/index.js'
import { getToken } from '@/utils/auth'

axios.interceptors.response.use(
  response => {
    const res = response.data
    //这里根据自己的业务需求执行逻辑
    if (!res.success && res.appCode === '-0002') {
      router.replace({ path: '/login' })
    }
    return response
  },
  error => {
    consoel.log('报错了:' + error.message)
    return Promise.reject(error)
  }
)

最后封装axios请求

export default {
  http(methods, url, params) {
      if (methods === 'get') {
          url.indexOf('?') > -1 ? (url += '&' + new Date().getTime()) : (url += '?' + new Date().getTime())
      }
      return axios({
          method: methods,
          url: url,
          params: (methods === 'get' || methods === 'delete') && params,
          data: methods === 'post' && params
      })
      .then(res => {
          if (res.status !== 200) checkStatus(res)
          return res
      })
      .catch(err => {
          throw err
      })
  },
}

下面是完整的axi.js文件

import router from '@/router/index.js'
import axios from 'axios'
import cookies from 'js-cookie'
import store from '@/store/index.js'
import { getToken } from '@/utils/auth'

axios.defaults.timeout = 15000
//`withCredentials`选项表明了是否是跨域请求
axios.defaults.withCredentials = false
//设置默认请求头
//成功的处理错误的回调函数
function checkStatus(response) {
  console.log(response)
}
axios.interceptors.request.use(
  config => {
    if (store.getters.token) {
      // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
      config.headers['X-Token'] = getToken()
    }
    if (config.headers['Content-Type'] === undefined) { 
      config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
      const data = new FormData()
      for (const key in config.data) {
        if (config.data[key] != null && config.data[key] != undefined) {
          data.append(key, config.data[key])
        }
      }
    config.data = data
    }
    return config
  },
  error => {
    Promise.reject(error)
  }
)
axios.interceptors.response.use(
  response => {
    const res = response.data
    if (!res.success && res.appCode === '-0002') {
      router.replace({ path: '/login' })
    }
    return response
  },
  error => {
    consoel.log('报错了:' + error.message)
    return Promise.reject(error)
  }
)
export default {
  http(methods, url, params) {
      if (methods === 'get') {
          url.indexOf('?') > -1 ? (url += '&' + new Date().getTime()) : (url += '?' + new Date().getTime())
      }
      return axios({
          method: methods,
          url: url,
          params: (methods === 'get' || methods === 'delete') && params,
          data: methods === 'post' && params
      })
      .then(res => {
          if (res.status !== 200) checkStatus(res)
          return res
      })
      .catch(err => {
          throw err
      })
  },
}

request.js

这个文件的话就是后端提供的接口API了,将用到的接口都写到这里方便管理。

import $http from './axi'
import axios from 'axios'
export default {
  getList(obj) {
    return $http.http('get', `/api/getlist`, obj)
  },
  ...
}

全局注册

注册变量

import { createApp } from 'vue'
import App from './App.vue'
import api from '@/utils/request' 

const app = createApp(App)
app.config.globalProperties.$api = api

可见Vue3不再使用Vue2之前那样的 Vue.prototype.$api = api 原型注入。并且注意的是,Vue3使用全局变量也是有所不同:

首先需要用的是Vue3的 getCurrentInstance 模块,先看看它给来了哪些数据。

通过展开发现我们注册的全局变量存在appContext中,为此使用全局变量就变成了这样:

这里我采用 解构赋值 的方式获取$api

import { getCurrentInstance } from "vue"
const { appContext: { config: { globalProperties: { $api } } } } = getCurrentInstance()

从上也可以看到我们之前写的接口API,所以现在只需要这样写就能请求接口了。

import { onMounted,getCurrentInstance } from "vue"
...
const { appContext: { config: { globalProperties: { $api } } } } = getCurrentInstance()
function getlist() {
  $api.getlist({
    currentPage: 1,
    pageSize: 10,
  }).then(res => {
    console.log(res)
  })
}
onMounted(() => {
  getlist()
})
....

onMounted 是替代之前Vue2的mounted生命周期,在组件渲染加载完成时执行回调。

注册组件

首先在main.js引入通用组件,然后使用component('xxx',xxx)方式注册

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './index.css'
import api from '@/utils/request' 
import HelloWorld from "@/components/HelloWorld.vue"
const app = createApp(App)
app.config.globalProperties.$api = api

app.component('HelloWorld',HelloWorld).use(router).use(store).mount('#app')

这样项目内的其他页面就不需要引入和注册了,直接使用<HelloWorld />就可以了。

script setup语法糖

谈setup语法糖之前我们需要升级Vue3的版本,目前是3.0.4,这个版本的setup还属于试验性阶段,只有3.2.x才是正式发行版。这里我选用3.2.2版本(目前最新3.2.4)

yarn add vue@^3.2.2

写之前先来了解下什么是setup语法糖?🤔️

其实就是省略了很多的代码—内部处理了。比如将变量暴露到顶部全局,就不需要使用return{}了。另外就是页面引入组件,也不需要手动注册组件了。最重要的是带来了很多的新特性。

直接来对比下先前代码:

<template>
  <div>我是vuex过来的数据:{{count}}</div>
  <div>我是vuex过来的数据2{{count2}}</div>
  <button @click="handleAddCount">我来修改Count的数据,每次加1</button>
  <About />
</template>

<script>
import { computed, getCurrentInstance } from "vue"
import { useStore } from 'vuex'
import About from "@/views/about/index.vue"
export default {
  components:{ About },
  setup() {
    const store = useStore()
    const count = computed(() => store.state.count)
    const count2 = computed(() => store.getters.getCount)
    function handleAddCount() {
      store.dispatch('addAction', 1)
    }
    return { count, handleAddCount, count2 }
  }
}
</script>

用了setup语法糖后:

<template>
  <div>我是vuex过来的数据:{{count}}</div>
  <div>我是vuex过来的数据2{{count2}}</div>
  <button @click="handleAddCount">我来修改Count的数据,每次加1</button>
  <About />
</template>

<script setup>
import About from "@/views/about/index.vue"
import { computed, getCurrentInstance } from "vue"
import { useStore } from 'vuex'

const store = useStore()
const count = computed(() => store.state.count)
const count2 = computed(() => store.getters.getCount)
function handleAddCount() {
  store.dispatch('addAction', 1)
}
</script>

可以看到省去了很多的代码。代码顿时清爽了许多。

也许有人会问:“Vue2的那些Options API不是挺好的吗?怎么要换Compostion API呢?”

我的个人理解是:当我们初次接触新技术、新事物的时候,我们需要约束来规范自己的行为才能更快的融入其中,但是,当我们熟悉之后,我们希望的是更加自由的DIV
Vue3之前我们需要被约束,在对应的地方完成我们想要做的事,但到了Vue3我们需要更多的自由,给足空间发挥你的思想。

Vue3会一只迭代,我们也需要一直学习和进步。不单是我们,尤大大这几天就直接更新了Vue3.2.2版本,他为了前端开发者们操碎了心,我们也为了他持续痴迷爆肝。

真应了那句:“我待你如初恋,你却虐我千万遍”。

虽说如此,但还是要学啊😭 。继续前行吧💪

那么3.2.x版本相比3.0.0版本有哪些更改的地方?(上面的setup语法糖是其一新增)

舍弃了部分导入模块

  • defineProps
  • defineEmits

在最新的版本它们已不再需要导入了,直接变成了编辑器宏,内置的模块。

单文件驱动的CSS变量

这里安装下sass(这个跟单文件驱动的CSS变量没有关联,只不过演示的时候用了sass而已)

<script setup>
  import { reactive,ref } from "vue"
  const mainStyle = reactive({
    color: 'red',
    fontSize: '20px'
  })
  const fontWeight = ref('600')
</script>

<style lang="scss" scoped>
.my_style {
  color: v-bind("mainStyle.color");
  font-size: v-bind("mainStyle.fontSize");
  font-weight: v-bind(fontWeight);
}
</style>

使用 v-bind 语法绑定样式。

接入UI框架

做管理后台用的最多最多的应该是ElementUi吧,所以这次选用最新版的ElementUi-plus。

yarn add element-plus -D

main.js引入

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

import './index.css'
import api from '@/utils/request' 
import HelloWorld from "@/components/HelloWorld.vue"
const app = createApp(App)
app.config.globalProperties.$api = api

app.component('HelloWorld',HelloWorld).use(ElementPlus).use(router).use(store).mount('#app')

使用的时候会发现ElementPlus默认变成了英文包,这真是大写的绝😓

并且转化中文的方式还有所不同。

我们在 1.0.2-beta.59(包含59) 之后的国际化按需引入有破坏性变动,请往下查看,该变动不适用于 1.0.2-beta.58 之前的版本

1.0.2-beta.59之前的中文包配置

先来看看59之前应该怎么写:

import ElementPlus from 'element-plus'
import locale from 'element-plus/lib/locale/lang/zh-cn'
import 'dayjs/locale/zh-cn'
const app = createApp(App)
app.use(ElementPlus, {locale}).mount('#app')

其实还是挺麻烦的😓

59之后怎么配置中文包呢?(包含59)

1.0.2-beta.59(包含59)之后的中文包配置

main.js中引入和注册不变的情况下

...
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
const app = createApp(App)
app.use(ElementPlus).mount('#app')

需要在App.vue中处理如下:

<template>
  <el-config-provider :locale="locale">
    <router-view />
  </el-config-provider>
</template>

<script setup>
import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/lib/locale/lang/zh-cn'
import { ref } from "vue"
const locale = ref(zhCn)
</script>

这里其实就是通过 ConfigProvider 的方式来使用,它是被用来提供全局的配置选项,让你的配置能够在全局都能够被访问到。

另外这里也使用了 setup语法糖,不用语法糖的话相应修改一下即可。

到这本次的分享教程就到此结束了。有需要源码的小伙伴可以在Github查看 Vue3+vite+setup源代码 [git@github.com:hzequn/course-vue3-vite.git]

👏 欢迎小伙伴们留言区留下你的意见。

推荐阅读:

  • 知乎热点:国家何时整治程序员的高薪现象?
  • JS数组reduce的妙用,收藏等于学会!
  • Vue2与Vue3组件通信方式总结!
  • 你正在使用哪个版本的JS特性?
  • 你还在为Node版本管理烦恼?

欢迎关注公众号前端发现

在这里插入图片描述

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号