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

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

bigegpt 2024-08-29 11:26 1 浏览

前言

小希这次带来了进阶版的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

相关推荐

得物可观测平台架构升级:基于GreptimeDB的全新监控体系实践

一、摘要在前端可观测分析场景中,需要实时观测并处理多地、多环境的运行情况,以保障Web应用和移动端的可用性与性能。传统方案往往依赖代理Agent→消息队列→流计算引擎→OLAP存储...

warm-flow新春版:网关直连和流程图重构

本期主要解决了网关直连和流程图重构,可以自此之后可支持各种复杂的网关混合、多网关直连使用。-新增Ruoyi-Vue-Plus优秀开源集成案例更新日志[feat]导入、导出和保存等新增json格式支持...

扣子空间体验报告

在数字化时代,智能工具的应用正不断拓展到我们工作和生活的各个角落。从任务规划到项目执行,再到任务管理,作者深入探讨了这款工具在不同场景下的表现和潜力。通过具体的应用实例,文章展示了扣子空间如何帮助用户...

spider-flow:开源的可视化方式定义爬虫方案

spider-flow简介spider-flow是一个爬虫平台,以可视化推拽方式定义爬取流程,无需代码即可实现一个爬虫服务。spider-flow特性支持css选择器、正则提取支持JSON/XML格式...

solon-flow 你好世界!

solon-flow是一个基础级的流处理引擎(可用于业务规则、决策处理、计算编排、流程审批等......)。提供有“开放式”驱动定制支持,像jdbc有mysql或pgsql等驱动,可...

新一代开源爬虫平台:SpiderFlow

SpiderFlow:新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫。-精选真开源,释放新价值。概览Spider-Flow是一个开源的、面向所有用户的Web端爬虫构建平台,它使用Ja...

通过 SQL 训练机器学习模型的引擎

关注薪资待遇的同学应该知道,机器学习相关的岗位工资普遍偏高啊。同时随着各种通用机器学习框架的出现,机器学习的门槛也在逐渐降低,训练一个简单的机器学习模型变得不那么难。但是不得不承认对于一些数据相关的工...

鼠须管输入法rime for Mac

鼠须管输入法forMac是一款十分新颖的跨平台输入法软件,全名是中州韵输入法引擎,鼠须管输入法mac版不仅仅是一个输入法,而是一个输入法算法框架。Rime的基础架构十分精良,一套算法支持了拼音、...

Go语言 1.20 版本正式发布:新版详细介绍

Go1.20简介最新的Go版本1.20在Go1.19发布六个月后发布。它的大部分更改都在工具链、运行时和库的实现中。一如既往,该版本保持了Go1的兼容性承诺。我们期望几乎所...

iOS 10平台SpriteKit新特性之Tile Maps(上)

简介苹果公司在WWDC2016大会上向人们展示了一大批新的好东西。其中之一就是SpriteKitTileEditor。这款工具易于上手,而且看起来速度特别快。在本教程中,你将了解关于TileE...

程序员简历例句—范例Java、Python、C++模板

个人简介通用简介:有良好的代码风格,通过添加注释提高代码可读性,注重代码质量,研读过XXX,XXX等多个开源项目源码从而学习增强代码的健壮性与扩展性。具备良好的代码编程习惯及文档编写能力,参与多个高...

Telerik UI for iOS Q3 2015正式发布

近日,TelerikUIforiOS正式发布了Q32015。新版本新增对XCode7、Swift2.0和iOS9的支持,同时还新增了对数轴、不连续的日期时间轴等;改进TKDataPoin...

ios使用ijkplayer+nginx进行视频直播

上两节,我们讲到使用nginx和ngixn的rtmp模块搭建直播的服务器,接着我们讲解了在Android使用ijkplayer来作为我们的视频直播播放器,整个过程中,需要注意的就是ijlplayer编...

IOS技术分享|iOS快速生成开发文档(一)

前言对于开发人员而言,文档的作用不言而喻。文档不仅可以提高软件开发效率,还能便于以后的软件开发、使用和维护。本文主要讲述Objective-C快速生成开发文档工具appledoc。简介apple...

macOS下配置VS Code C++开发环境

本文介绍在苹果macOS操作系统下,配置VisualStudioCode的C/C++开发环境的过程,本环境使用Clang/LLVM编译器和调试器。一、前置条件本文默认前置条件是,您的开发设备已...