目的:利用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无法辨认别名@
要在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新特性:项目引用
这个单独拆分出来的配置文件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'] },
我们看分布图有四个大模块(最左侧的一条是其他页面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 |