百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

封装搭建Vue3 + Vite项目框架进阶版,值得学习收藏【前端工程化】

ccwgpt 2024-10-25 10:53 39 浏览 0 评论

前言

小希这次带来了进阶版的Vue3 + Vite项目框架的封装搭建

本文主要的切入点有

搭建过程

多入口打包

一般情况下,项目开发只有一个入口,只需要配置一个入口,一个项目

但有时多个同业务、同类型的项目,有很多可以复用的业务,组件,工具类等,就可以放在同一个代码库里进行维护,不用新建多个代码库

每个项目都有自己独立的入口,可以独立打包并进行部署,低耦合,不会相互影响,同时还可以复用相同的组件,业务等,可以大大地提高开发效率和后期的维护

调整项目结构目录

如上图所示,有两个项目,分别是app1,app2,每个项目都有自己独有的main.ts入口文件,App.vue文件,以及路由,仓库pinia,组件等,同时也有共有的组件,utils工具类等

配置过程

在package.json中,将下图单入口的配置

改为

{
  "scripts": {
    "dev:app1": "vite serve src/app1/ --config ./vite.config.ts",
    "dev:app2": "vite serve src/app2/ --config ./vite.config.ts",
    "build:app1": "vue-tsc && vite build",
    "build:app2": "vue-tsc && vite build"
  },


这样配置可实现项目的独立运行,独立打包

在vite.config.ts中配置:

/* 项目名称 */
//采用这种方式可以动态获取项目名称,当然,如果项目少可以手动配置
let appName = process.env.npm_lifecycle_event
appName = appName.slice(appName.indexOf(':') + 1) //app1、app2

export default defineConfig({
  root: `./src/${appName}/`,
  build: {
    rollupOptions: {
      input: {
        [appName]: path.resolve(__dirname, `src/${appName}/index.html`)
      },
      output: {
        chunkFileNames: 'js/[name]-[hash].js',
        entryFileNames: 'js/[name]-[hash].js',
        assetFileNames: '[ext]/[name]-[hash].[ext]',
      }
    }
  }
})


测试项目的运行和打包构建

打包构建

pnpm build:app1
pnpm build:app2

自动化生成项目基础模版

基于多入口打包,也就是一个代码库同时维护多个同类型的项目情况下,可以通过配置实现自动化生成项目基础模板,这样,当需要在代码库新建一个新项目时,可以通过命令行快速创建

前置工具

inquirer

这个插件用来询问用户输入项目名称,这是一个比较在处理命令行交互比较常见的库

主要用于实现命令行交互式界面。帮助我们与用户进行交互式交流

它有几个特点:提供错误反馈,询问问题,解析输入,验证答案

详细可参考 命令行交互工具inquirer

安装

pnpm add  inquirer@^8.0.0 -S


配置过程

在package.json里添加

"scripts": {
    "init-app": "node ./src/utils/initApp/index.ts"
}


当执行这个命令时,会自动去执行,在本地utils文件夹下的initApp文件里的js脚本,在src目录下会自动生成一个新的文件夹(项目)

在utils下新增initApp文件夹以及index.ts和temlate

在index.ts添加以下代码

#!/usr/bin/env node
console.log('您正在创建项目')
const path = require('path')
const fs = require('fs')
const inquirer = require('inquirer')
const stat = fs.stat
const targetDir = path.resolve(__dirname, './template')
//复制文件目录
const copyFile = (targetDir, resultDir) => {
  // 读取文件、目录
  fs.readdir(targetDir, function (err, paths) {
    if (err) {
      throw err
    }
    paths.forEach(function (p) {
      const target = path.join(targetDir, '/', p)
      const res = path.join(resultDir, '/', p)
      let read
      let write
      stat(target, function (err, statsDta) {
        if (err) {
          throw err
        }
        if (statsDta.isFile()) {
          read = fs.createReadStream(target)
          write = fs.createWriteStream(res)
          read.pipe(write)
        } else if (statsDta.isDirectory()) {
          fs.mkdir(res, function () {
            copyFile(target, res)
          })
        }
      })
    })
  })
}

const question = [
  {
    type: 'input',
    name: 'name',
    message: '请输入项目名称:'
  }
]

const createProject = () => {
  // 询问用户问题
  inquirer
    .prompt(question)
    .then(({ name }) => {
      // name 为输入的项目名称
      name = name.trim()
      if (!name) {
        console.log('项目目录不能为空')
        // 如果输入空,继续询问
        createProject()
        return false
      }
      // 目标路径,要放在module目录下
      const resultDir = path.resolve(__dirname, '../../', name)
      // fs.access()方法用于测试文件是否存在
      fs.access(resultDir, function (err, data) {
        if (err) {
          // 创建文件
          fs.mkdir(resultDir, function (err, data) {
            if (err) {
              throw err
            }
            // 复制模版文件
            copyFile(targetDir, resultDir)
          })
          console.log(`${name} 项目已创建成功`)
        } else {
          console.log(`${name} 项目目录已存在,请输入其他名称`)
          // 不存在,继续询问
          createProject()
        }
      })
    })
    .catch((err) => {
      console.log(err)
    })
}
createProject()


注:此代码copy==> Vue3项目框架搭建封装,一次学习,终身受益【万字长文,满满干货】

在temlate文件夹下新增项目所需要的文件目录,main.ts以及App.vue是必须的,因为它是独立的项目

使用

app3项目自动生成

持久化pinia仓库数据

数据存储在缓存(内存)中,优点读写更快,可以保存任意的js类型数据和对象,比如当我们刷新浏览器的时候,数据会丢失,所以需要实现pinia持久化

持久化插件pinia-plugin-persist

安装

pnpm add pinia-plugin-persist


import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPersist from 'pinia-plugin-persist'

const pinia = createPinia()
pinia.use(piniaPersist)

createApp({})
  .use(pinia)
  .mount('#app')


使用

// store/use-user-store.ts
import { defineStore } from 'pinia'

export const useUserStore = defineStore('storeUser', {
  state: () => {
    return {
      firstName: 'S',
      lastName: 'L',
      accessToken: 'xxxxxxxxxxxxx'
    }
  },
  actions: {
    setToken (value: string) {
      this.accessToken = value
    }
  },
  persist: {
    enabled: true,
    //这里可以单独给每个字段配置存储的形式sessionStorage/localStorage
    //paths配置state里的字段,不同的数据采取不同的存储方式
    strategies: [
      { storage: sessionStorage, paths: ['firstName', 'lastName'] },
      { storage: localStorage, paths: ['accessToken'] },
    ],

  }
})


strategies 字段说明:

属性

描述

key

自定义存储的 key,默认是 store.$id

storage

可以指定localStorage/sessionStorage,或者自定义存储类型,默认为 sessionStorage

paths

state 中的字段名,按组打包储存

也可以自定义存储类型,更多具体配置戳pinia-plugin-persist插件官网地址

源码解析

核心是通过 store.$subscribe去监听仓库数据,当仓库数据发生变化时会触发回调,更改本地缓存数据,当刷新后就会从本地缓存取出相关的数据

import { PiniaPluginContext } from 'pinia'


type Store = PiniaPluginContext['store']; //pinia插件上下文
type PartialState = Partial<Store['$state']>;

//调用函数将仓库数据存储到本地

export const updateStorage = (strategy: PersistStrategy, store: Store) => {
  const storage = strategy.storage || sessionStorage //可以自定义存储类型,默认为sessionStorage
  const storeKey = strategy.key || store.$id   //可以自定义存储的 key,默认是 store.$id 

 //判断是否有配置paths,如果没有就缓存一整个仓库中的state
  if (strategy.paths) {
  
  //遍历paths里面的字段,并通过 store.$state[key]获取相应的数据
    const partialState = strategy.paths.reduce((finalObj, key) => {
      finalObj[key] = store.$state[key]
      return finalObj
    }, {} as PartialState)
    
    //存储到本地
    storage.setItem(storeKey, JSON.stringify(partialState))
  } else {
    storage.setItem(storeKey, JSON.stringify(store.$state))
  }
}

export default ({ options, store }: PiniaPluginContext): void => {
    //判断enabled是否为true
    
  if (options.persist?.enabled) {
    const defaultStrat: PersistStrategy[] = [{
      key: store.$id,
      storage: sessionStorage,
    }]

    const strategies = options.persist?.strategies?.length ? options.persist?.strategies : defaultStrat

    strategies.forEach((strategy) => {
      const storage = strategy.storage || sessionStorage
      const storeKey = strategy.key || store.$id
      
     //根据key判断是否在本地缓存中,如果在刷新后会从本地缓存中将数据赋给pinia仓库的state
      const storageResult = storage.getItem(storeKey)

     // 如果本地中存在同步数据,更新仓库state数据
     //(比如浏览器刷新后会进行判断,如果有数据会赋值给pinia仓库的state,实现pinia持久化)
      if (storageResult) {
        store.$patch(JSON.parse(storageResult))
        updateStorage(strategy, store)
      }
    })
    
    //通过$subscribe监听state,仓库数据更改会触发回调同步更改本地数据
    store.$subscribe(() => {
      strategies.forEach((strategy) => {
        updateStorage(strategy, store)
      })
    })
  }
}


引入nprogess进度条

通过显示进度条的形式,来提高用户体验,可用在进入/离开路由时触发动画,也可在发接口时使用

安装

pnpm add nprogress -S


基本用法

只需调用start()和done()即可控制进度条。

NProgress.start();
NProgress.done();


使用场景

切换路由

router.beforeEach((to, from, next) => {
  NProgress.start()
  next()
})
router.afterEach(() => {
  NProgress.done()
})


发请求时

// axios请求拦截器
axios.interceptors.request.use(
config => {
    NProgress.start() // 设置加载进度条(开始..)
    return config
},
error => {
    return Promise.reject(error)
}
)
// axios响应拦截器
axios.interceptors.response.use(
function(response) {
    NProgress.done() // 设置加载进度条(结束..)
    return response
},
function(error) {
    return Promise.reject(error)
}
)


其它详细配置请戳官网:ricostacruz.com/nprogress/

viewport 适配方案 - postCSS插件

介绍

PostCSS 是一种 JavaScript 工具,可将你的 CSS 代码转换为抽象语法树 (AST),然后提供 API(应用程序编程接口)用于使用 JavaScript 插件对其进行分析和修改。

Autoprefixer主要功能是解析CSS并使用Can I Use中的值向CSS规则添加供应商前缀。以兼容各种浏览器,部分CSS属性需要加上不同的前缀以兼容不同的浏览器。通过配置Autoprefixer,自动为CSS属性添加对应浏览器的前缀。

postcss-px-to-viewport 用于将单位为 px 的尺寸转换为视口单位(vw, vh, vmin, vmax)

下面用到Autoprefixer和postcss-px-to-viewport这两个插件进行viewport适配

PostCSS 配置

安装

pnpm add postcss-px-to-viewport -D
pnpm add  autoprefixer -D


创建postcss.config.js并配置

// postcss.config.js
module.exports = () => {
  return {
    plugins: {
      autoprefixer: {},
      'postcss-px-to-viewport': {
        unitToConvert: 'px', // 需要转换的单位,默认为"px"
        viewportWidth: 1920, // 设计稿的视口宽度
        unitPrecision: 5, // 单位转换后保留的精度
        propList: ['*'], // 能转化为vw的属性列表
        viewportUnit: 'vw', // 希望使用的视口单位
        fontViewportUnit: 'vw', // 字体使用的视口单位
        selectorBlackList: [], // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。
        minPixelValue: 1, // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
        mediaQuery: false, // 媒体查询里的单位是否需要转换单位
        replace: true, //  是否直接更换属性值,而不添加备用属性
        exclude: undefined, // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
        include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换
        landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
        landscapeUnit: 'vw', // 横屏时使用的单位
        landscapeWidth: 1920 // 横屏时使用的视口宽度
      }
    }
  }
}


效果如下

不同视口宽度,界面会响应性变化


作者:小希学前端
链接:https://juejin.cn/post/7327965216826032154

相关推荐

一个基于.Net Core遵循Clean Architecture原则开源架构

今天给大家推荐一个遵循CleanArchitecture原则开源架构。项目简介这是基于Asp.netCore6开发的,遵循CleanArchitecture原则,可以高效、快速地构建基于Ra...

AI写代码翻车无数次,我发现只要提前做好这3步,bug立减80%

写十万行全是bug之后终于找到方法了开发"提示词管理助手"新版本那会儿,我差点被bug整崩溃。刚开始两周,全靠AI改代码架构,结果十万行程序漏洞百出。本来以为AI说没问题就稳了,结果...

OneCode低代码平台的事件驱动设计:架构解析与实践

引言:低代码平台的事件驱动范式在现代软件开发中,事件驱动架构(EDA)已成为构建灵活、松耦合系统的核心范式。OneCode低代码平台通过创新性的注解驱动设计,将事件驱动理念深度融入平台架构,实现了业务...

国内大厂AI插件评测:根据UI图生成Vue前端代码

在IDEA中安装大厂的AI插件,打开ruoyi增强项目:yudao-ui-admin-vue31.CodeBuddy插件登录腾讯的CodeBuddy后,大模型选择deepseek-v3,输入提示语:...

AI+低代码技术揭秘(二):核心架构

本文档介绍了为VTJ低代码平台提供支持的基本架构组件,包括Engine编排层、Provider服务系统、数据模型和代码生成管道。有关UI组件库和widget系统的信息,请参阅UI...

GitDiagram用AI把代码库变成可视化架构图

这是一个名为gitdiagram的开源工具,可将GitHub仓库实时转换为交互式架构图,帮助开发者快速理解代码结构。核心功能一键可视化:替换GitHubURL中的"hub...

30天自制操作系统:第六天:代码架构整理与中断处理

1.拆开bootpack.c文件。根据设计模式将对应的功能封装成独立的文件。2.初始化pic:pic(可编程中断控制器):在设计上,cpu单独只能处理一个中断。而pic是将8个中断信号集合成一个中断...

AI写代码越帮越忙?2025年研究揭露惊人真相

近年来,AI工具如雨后春笋般涌现,许多人开始幻想程序员的未来就是“对着AI说几句话”,就能轻松写出完美的代码。然而,2025年的一项最新研究却颠覆了这一期待,揭示了一个令人意外的结果。研究邀请了16位...

一键理解开源项目:两个自动生成GitHub代码架构图与说明书工具

一、GitDiagram可以一键生成github代码仓库的架构图如果想要可视化github开源项目:https://github.com/luler/reflex_ai_fast,也可以直接把域名替换...

5分钟掌握 c# 网络通讯架构及代码示例

以下是C#网络通讯架构的核心要点及代码示例,按协议类型分类整理:一、TCP协议(可靠连接)1.同步通信//服务器端usingSystem.Net.Sockets;usingTcpListene...

从复杂到优雅:用建造者和责任链重塑代码架构

引用设计模式是软件开发中的重要工具,它为解决常见问题提供了标准化的解决方案,提高了代码的可维护性和可扩展性,提升了开发效率,促进了团队协作,提高了软件质量,并帮助开发者更好地适应需求变化。通过学习和应...

低代码开发当道,我还需要学习LangChain这些框架吗?| IT杂谈

专注LLM深度应用,关注我不迷路前两天有位兄弟问了个问题:当然我很能理解这位朋友的担忧:期望效率最大化,时间用在刀刃上,“不要重新发明轮子”嘛。铺天盖地的AI信息轰炸与概念炒作,很容易让人浮躁与迷茫。...

框架设计并不是简单粗暴地写代码,而是要先弄清逻辑

3.框架设计3.框架设计本节我们要开发一个UI框架,底层以白鹭引擎为例。框架设计的第一步并不是直接撸代码,而是先想清楚设计思想,抽象。一个一个的UI窗口是独立的吗?不是的,...

大佬用 Avalonia 框架开发的 C# 代码 IDE

AvalonStudioAvalonStudio是一个开源的跨平台的开发编辑器(IDE),AvalonStudio的目标是成为一个功能齐全,并且可以让开发者快速使用的IDE,提高开发的生产力。A...

轻量级框架Lagent 仅需20行代码即可构建自己的智能代理

站长之家(ChinaZ.com)8月30日消息:Lagent是一个专注于基于LLM模型的代理开发的轻量级框架。它的设计旨在简化和提高这种模型下代理的开发效率。LLM模型是一种强大的工具,可以...

取消回复欢迎 发表评论: