vite + vue3多页面配置记载references,loadEnv等

源码 2024-9-10 15:10:09 54 0 来自 中国
目的:利用vite创建vue3项目记载细节点
上篇vue2/vue3 + webpack多页面碰到的题目和思考我们利用vue-cli搭建项目都是利用webpack打包的,现在对比一下vite感受一下极速开辟体验
增:下一篇vite + vue3 多页面实战优化续集:eslint+lint-staged+husky+stylelint
第一部分:项目底子配置ts相干: lib, references,loadEnv

说到vue3,不得不提ts,说到ts,肯定要先到  官方文档相识tsconfig.json配置的意思,这里我以为故意思的就是references/lib。
我们通过处理惩罚一些告诫来相识配置:
比方你利用了vue主动导入插件:unplugin-auto-import/vite, 组件主动导入unplugin-vue-components/vite,大概会碰到以下题目

  • 在自定义ts文件中引入资源,ts无法辨认别名@

    1.png
要在tsconfig.json配置paths
记得将以上主动导入插件天生的文件auto-imports.d.ts,components.d.ts放入到 tsconfig.json的include数组中
// vite.config.tsimport { resolve } from 'path'export default defineConfig({  plugins,resolve: {  alias: {    '@': resolve(__dirname, 'src')  }}})// tsconfig.json"compilerOptions": {    "baseUrl": ".",    "paths":{      "@": ["src"],      "@/*": ["src/*"],    }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "auto-imports.d.ts",  "components.d.ts"],  "references": [{ "path": "./tsconfig.node.json" }],  },

  • 上面有个references,相当于拆分了tsconfig配置到tsconfig.node.json,关于这个配置我们看官方文档ts3.0新特性:项目引用
2.png 这个单独拆分出来的配置文件include包罗vite.config.ts,分析只是负责编译 vite 的配置文件。
我在根目次新建了一个build文件夹,拆分了plugin的引入和多页面配置,这里赤色告诫提示要在tsconfig.node.json的include中参加文件
// tsconfig.node.json{  "compilerOptions": {    "composite": true,    "module": "esnext",    "moduleResolution": "node",    "allowSyntheticDefaultImports": true  },  "include": ["vite.config.ts", "build/*.ts"]}

  • src/env.d.ts中我们可以看到有资助ts辨认vue文件的代码,尚有三个斜线和reference,这个reference是不是和上面我们tsconfig.json配置文件中的references长的很像
/// <reference types="vite/client" />declare module '*.vue' {  import type { DefineComponent } from 'vue'  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types  const component: DefineComponent<{}, {}, any>  export default component}interface ImportMetaEnv {  readonly VITE_APP_TITLE: string  // 更多情况变量...}src/env.d.ts 中的三斜线指令官方文档分析 /// <reference types="vite/client" />
三斜线引用告诉编译器在编译过程中用types情势引入的额外的文件vite/client.d.ts,这个文件内里就是vite帮我们定义的各种常用范例定义,好比css,图片等。下图可以看到又用path的情势引入了./types/importMeta.d.ts,还引入了dom相干声明<reference lib="dom" />
想一想:这个lib在tsconfig.json/compilerOptions配置项目中也有
继承深入到types/importMeta.d.ts发现了可以全局利用的一些声明ImportMetaEnv,GlobOptions
// vite/client/types/importMeta.d.tsinterface ImportMetaEnv {  [key: string]: any  BASE_URL: string  MODE: string  DEV: boolean  PROD: boolean  SSR: boolean}综上我们可以发现

  • vite官方文档情况变量告诉我们在配置多个情况变量的时间可以在src/env.d.ts中再次声明ImportMetaEnv插入自己的全局变量。原来是归并了内部范例,到达新增全局变量的作用。新增情况变量记得VITE_ 开头
// 项目根目次新增的 .env.development文件VITE_PROJECT_ENV = 'development'VITE_APP_URL = "https://app-api-0.com/"

  • <reference lib="dom" /> 中用到的lib,types 和tsconfig.json配置文件中的references/compilerOptions中的"lib": ["esnext", "dom"],types属性一样的,只是差别文件场景差别的利用方式而已。
(1) 在d.ts文件中
声明对某个包的依赖用到了types: /// <reference types="vite/client" />
用路径声明依赖的用到了path: /// <reference path="./types/importMeta.d.ts" />
(2) 在.ts文件里声明一个对@types包的依赖,可以利用–types下令行选项或在tsconfig.json里指定
/// <reference types="node" />

  • 官方文档-编译选项告诉我们:target 选项用来指定最终代码运行情况所支持的 JavaScrpt 语法的版本; lib 选项的值默认继承自 target:es5,默认注入target值版本es5库和dom 库。所以我们才华在默认项目中看到vite帮我们配置了"lib": ["esnext", "dom"]
  • 关于配置文件references,我们可以看到在vite项目拆分了出了tsconfig.node.json配置文件,专门用来负责vite.config.ts配置相干编译工作,结构清楚。
    它更告急的作用references官方文档已经告诉我们了: 一套配置,多个工程,修改时只编译子项目自己,明显地加快范例查抄和编译。  更过细的举例文章
    所以:拆开后,vite项目中修改vite.config.ts配置并不会导致整个src项目重新编译ts,仅仅只是触发reload
关于情况变量,这里汇总一波


  • 想在vite.config.ts中利用情况变量,要用vite提供的loadEnv(mode, process.cwd())
单页面应用在src目次下项目中用情况变量可以利用import.meta.env检察.env文件中的自定义情况变量,大概利用import.meta.glob(雷同webpack的require.context)读取文件,但是在vite.config.ts配置文件中import.meta是空的
loadEnv的第一参数mode那里有?两种方式
(1)官网情况变量告诉我们可以直接在vite.config.ts中将defineConfig参数写成函数,函数就有mode参数可用
下面代码中有define,我们在下面讲
export default defineConfig(({ mode }) => { // 根据当前工作目次中的 `mode` 加载 .env 文件return {    // vite config    define: { // 设置第三个参数为 '' 来加载全部情况变量,而不管是否有 `VITE_` 前缀。        'process.env':  loadEnv(mode, process.cwd(), '')    }  }})(2)mode值也可以共同scripts下令从process.argv中拿,现实上defineConfig的mode参数也是拿的这个
tips:
a. 注意下面的 --mode development下令在最背面,方便process.argv取其值;
b. 注意 --mode development和新建的.env.development文件同名
// package.json"scripts": {    "dev": "vite --host --mode development",    "test": "vue-tsc --noEmit && vite build --mode test",    "build": "vue-tsc --noEmit && vite build --mode production",    "preview": "vite preview"  }import { loadEnv } from 'vite'const mode = process.argv[process.argv.length - 1]console.log(mode)const env = loadEnv(mode, process.cwd())// env// {//  VITE_PROJECT_ENV: 'development',//  VITE_APP_URL: 'https://app-api-0.com/'// }

  • 上面代码中提到define,定义全局常量。这在多页面应用中非常有用
在多页面中你会发现:process没有,报错:process is not defined,import.meta.env也没有归并自定义的变量
define: { // 设置第三个参数为 '' 来加载全部情况变量,而不管是否有 `VITE_` 前缀。        'process.env':  loadEnv(mode, process.cwd(), '')    }全局利用const {VITE_APP_URL} = process.env第二部分:多页面配置

目次结构照旧一样的,views下面多个页面都有自己的main.ts/index.vue
├── public└── build // 多页面天生└── src    ├── api  // 哀求    ├── assets // 静态资源    ├── components  // 公共组件    ├── config  // 公用类    └── views  // 页面         └── home  // 页面                ├── lang  // 多语言                ├── icons // svg图标                ├── components  // 组件                ├── router  // 路由,选择性须要                ├── store  // 选择性须要                ├── main.ts  //                 ├── index.vue // 第一步:照旧一样新建build文件夹下面有puligns.ts/getPages.ts,我们拆分plugins的引用以及多页面天生代码

//getPages.ts多页面入口html天生import glob from "glob";import fs from "fs";import { resolve } from 'path'const input = {}const mainEntry = []const iconDirs = []function getPages() {  // 遍历文件夹中含有main.ts的文件夹路径  const allEntry = glob.sync("./src/views/**/main.ts");  // 获取模板  const temp = fs.readFileSync("./index.html");  console.log('allEntry', allEntry)  // 创建多页面模板  allEntry.forEach((entry: string) => {    const pathArr = entry.split("/");    const name = pathArr[pathArr.length - 2];    // 判定文件是否存在    try {      fs.accessSync(`./src/views/${name}.html`);    } catch (err) {      console.log(`创建${name}.html文件`);      const index = temp.toString().indexOf("</body>");      const content =        temp.toString().slice(0, index) +        `<script type="module" src="./${name}/main.ts"></script>` +        temp.toString().slice(index);      fs.writeFile(`./src/views/${name}.html`, content, err => {        if (err) console.log(err);      });    }    // input中的配置    input[name] = resolve(`src/views/${name}.html`);    mainEntry.push(resolve(`src/views/${name}/main.ts`))    iconDirs.push(resolve(process.cwd(), `src/views/${name}/svg`))  });};getPages()console.log(input, mainEntry, iconDirs)export {input, mainEntry, iconDirs}关于路径小记:1. process.cwd() 永久是项目根目次,也就是package.json那一层2. __dirname 是当前工作目次,以上就是指build文件夹,比方以下代码在我新建的build文件夹中的getPage.ts以上代码

  • 我们以根目次的index.html为模版在src/views目次下天生了input(入口html路径), mainEntry(入口main.ts文件,给VConsole插件用), iconDirs(icon图标,vite-plugin-svg-icons插件要用)
  • 在每个页面html中插入script,引入自己的ts文件<script type="module" src="./${name}/main.ts"></script>
  • 记得删除模版,也就是根目次的index.html中的script标签先
我们看打印的效果

插件代码
import vue from '@vitejs/plugin-vue'import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'import styleImport, { VantResolve } from 'vite-plugin-style-import';import {visualizer} from "rollup-plugin-visualizer";import AutoImport from 'unplugin-auto-import/vite'import Components from 'unplugin-vue-components/vite'import viteCompression from "vite-plugin-compression";import WindiCSS from 'vite-plugin-windicss'import { viteVConsole } from 'vite-plugin-vconsole';import {mainEntry, iconDirs} from './getPage'export const getPlugins = (mode) => {  console.log('mode', mode)  return [    vue(),    WindiCSS({      scan: {        dirs: ['.'], // 当前目次下全部文件        fileExtensions: ['vue', 'js', 'ts'], // 同时启用扫描vue/js/ts    }    }),    visualizer(),    AutoImport({      imports: ['vue']    }),    Components(),    styleImport({      resolves: [VantResolve()],    }),    viteCompression({      ext: ".gz",      algorithm: "gzip",      deleteOriginFile: false    }),    viteVConsole({      entry: mainEntry,      localEnabled: mode !== 'production',      enabled: mode !== 'production',      config: {        maxLogNumber: 1000,        theme: 'dark'      }    }),    createSvgIconsPlugin({      iconDirs,      symbolId: 'icon-[dir]-[name]'    })  ]}2022.7.6增补

上面的vite-plugin-style-import 版本低于2.0.0
2.0.0最新的要修改一下,还会提示你没有consla这个依赖,安装一下
import { VantResolve, createStyleImportPlugin } from 'vite-plugin-style-import';styleImport 没了,换成下面的。createStyleImportPlugin({     resolves: [       VantResolve(),     ],     libs: [       // If you don’t have the resolve you need, you can write it directly in the lib, or you can provide us with PR       {         libraryName: 'vant',         esModule: true,         resolveStyle: (name) => {           return `../es/${name}/style`         },       },     ],   }),第二步:修改vite.config.ts

import { defineConfig } from 'vite'import { resolve } from 'path'import { input } from './build/getPage'import postCssPxToRem from "postcss-pxtorem"import { getPlugins } from './build/plugins'import { loadEnv } from 'vite'// const mode = process.argv[process.argv.length - 1]// https://vitejs.dev/config/export default defineConfig(({mode}) => {const plugins = getPlugins(mode)return {  base: './',  root: './src/views/',    publicDir: resolve(__dirname, 'public'),  plugins,  define: {      'process.env': loadEnv(mode, process.cwd())  },  css: {      postcss: {        plugins: [          postCssPxToRem({            rootValue: 37.5, // 1rem的巨细            propList: ['*'], // 须要转换的属性,这里选择全部都举行转换            selectorBlackList: ['.ig-','.dzg-header-','.za-'], // 忽略的选择器   .ig-  表示 .ig- 开头的都不会转换          })        ]      }  },  resolve: {    alias: {      '@': resolve(__dirname, 'src')    }  },  build: {    outDir: '../../dist', // 输出路径    assetsDir: 'static', // 静态文件目次        // 默认情况下 若 outDir 在 root 目次下, 则 Vite 会在构建时清空该目次。    emptyOutDir: true,    rollupOptions: {      input,      output: {        chunkFileNames: 'static/js/[name]-[hash].js',        entryFileNames: 'static/js/[name]-[hash].js',        assetFileNames: 'static/[ext]/[name]-[hash].[ext]',        manualChunks(id) {          // 单独打包第三方依赖          if (id.includes("node_modules")) {            return id              .toString()              .split("node_modules/")[1]              .split("/")[0]              .toString();          }        }      }    }  }}})有了以上的两步走修改,项目就可以正常跑起来访问了。
配置上,我们重点看几个配置root,publicDir,outDir,  rollupOptions /manualChunks

  • 由于在第一步我们以根目次下的index.html为模板,在src/views下天生了相应页面的html,那么项目root就不是指向根目次了,我们改为新天生的index.html目次 root: './src/views/', 如许启动项目就会主动打开这个新天生的src/views/index.html了
    同理:root变了,放静态资源的publicDir也要改一改,否则就获取到不放在public的静态资源了;outDir要改,不然打包天生的dist就跑到views下面了
  • manualChunks这个就雷同我们在webpack中用到的splitchunks,可以单独打包依赖项目。我们利用插件rollup-plugin-visualizer看一看打包后的包分布情况,
未配置manualChunks时看看分布图,以入口页面为维度打包了相应的一个js,我们可以看到被至少两个页面利用的,比方axios,vue-i18n等被打包到了virtual_svg-icons-register-150ef7c7.js ,vue被单独拆出来到windi-79315b5a.js, 这两个定名是随机取的你的依赖名称(真的狗)


我们再看配置manualChunks将依赖全部单个打包,每个依赖清楚可见。此中一个叫intlify的包被多个依赖引用,也被单独拆出来了

我们也可以根据须要将依赖包打包到一起,比方我将'vue', 'vue-i18n', 'axios'打包到一起名称叫做vandor,将vant单独打包,代码如下:
manualChunks: {       vandor: ['vue', 'vue-i18n', 'axios'],       vant: ['vant']  }, 7.png
我们看分布图有四个大模块(最左侧的一条是其他页面js):

  • vandor包全部在右侧,包罗了我想要的
  • vant在左下角,符合要求
  • login页面用到了jsencrypt,所以被打包到了login.js中
  • md5和svg插件被打包到一起,在名为virtual_svg-icons-register(名称取依赖名定名)的包中
其他小点:

  • less支持,只须要安装yarn add less -D即可
  • 项目假如提示没有glob,安装一个glob和@types/glob(范例支持) yarn add glob @types/glob -D
重点关于windicss ,由于多页面更改了root到src/views,要改两个地方,不然不会收效哦

(1)windi.config.ts 也要移动到src/views目次下
(2)plugins中windi.css的扫描范围scan也改一下
WindiCSS({      scan: {        dirs: ['.'], // 当前目次下全部文件        fileExtensions: ['vue', 'js', 'ts'], // 同时启用扫描vue/js/ts    }    }),至于其他,vue-router,pinia,eslint配置这里就不做记载了
以上就是全部实践记载,代码我放到gitee中。vite-vue3-multip-master
在下一篇vite + vue3 多页面实战优化续集:eslint+lint-staged+husky+stylelint中我对项目结构举行了优化,最新的项目结构放在了release分支,可直接用于项目实战vite-vue3-multip-master
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 18:23, Processed in 0.128811 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表