闽公网安备 35020302035485号

|-CHANGELOG.md |-LICENSE.md |-README.md |-bin | |-openChrome.applescript | |-vite.js |-client.d.ts |-package.json |-rollup.config.js #打包配置文件 |-scripts | |-patchTypes.js |-src | |-client #客户端 | | |-client.ts | | |-env.ts | | |-overlay.ts | | |-tsconfig.json | |-node #服务端 | | |-build.ts | | |-cli.ts #命令入口文件 | | |-config.ts | | |-constants.ts #常量 | | |-importGlob.ts | | |-index.ts | | |-logger.ts | | |-optimizer | | | |-esbuildDepPlugin.ts | | | |-index.ts | | | |-registerMissing.ts | | | |-scan.ts | | |-plugin.ts #rollup 插件 | | |-plugins #插件相关文件 | | | |-asset.ts | | | |-clientInjections.ts | | | |-css.ts | | | |-esbuild.ts | | | |-html.ts | | | |-index.ts | | | |-... | | |-preview.ts | | |-server | | | |-hmr.ts #热更新 | | | |-http.ts | | | |-index.ts | | | |-middlewares #中间件 | | | | |-... | | | |-moduleGraph.ts #模块间关系组装(树形) | | | |-openBrowser.ts #打开浏览器 | | | |-pluginContainer.ts | | | |-send.ts | | | |-sourcemap.ts | | | |-transformRequest.ts | | | |-ws.ts | | |-ssr | | | |-__tests__ | | | | |-ssrTransform.spec.ts | | | |-ssrExternal.ts | | | |-ssrManifestPlugin.ts | | | |-ssrModuleLoader.ts | | | |-ssrStacktrace.ts | | | |-ssrTransform.ts | | |-tsconfig.json | | |-utils.ts |-tsconfig.base.json |-types | |-...server 核心方法
import { createApp } from 'vue';
import App from './index.vue';
经服务端重写后,node_modules 文件夹下的三方包代码路径也会被拼接完整。import __vite__cjsImport0_vue from "/node_modules/.vite/vue.js?v=ed69bae0"; const createApp = __vite__cjsImport0_vue["createApp"]; import App from '/src/pages/back-sky/index.vue';
2.build 命令
文件路径:./build.ts ;
主要方法:build;{
"hash": "31d458ff",
"browserHash": "ed69bae0",
"optimized": {
"element-plus/lib/utils/dom": {
"file": "/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/element-plus_lib_utils_dom.js",
"src": "/Users/zcy/Documents/workspace/back-sky-front/node_modules/element-plus/lib/utils/dom.js",
"needsInterop": true
},
"element-plus": {
"file": "/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/element-plus.js",
"src": "/Users/zcy/Documents/workspace/back-sky-front/node_modules/element-plus/lib/index.esm.js",
"needsInterop": false
},
"vue": {
"file": "/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/vue.js",
"src": "/Users/zcy/Documents/workspace/back-sky-front/node_modules/vue/dist/vue.runtime.esm-bundler.js",
"needsInterop": true
},
......
}
}
}
模块解析
if (file === config.configFile || file.endsWith('.env')) {
// auto restart server 配置&环境文件修改则自动重启服务
debugHmr(`[config change] ${chalk.dim(shortFile)}`)
config.logger.info(
chalk.green('config or .env file changed, restarting server...'),
{ clear: true, timestamp: true }
)
await restartServer(server)
return
}
html 文件更新时,将会触发页面的重新加载。if (file.endsWith('.html')) { // html 文件更新
config.logger.info(chalk.green(`page reload `) + chalk.dim(shortFile), {
clear: true,
timestamp: true
})
ws.send({
type: 'full-reload',
path: config.server.middlewareMode
? '*'
: '/' + normalizePath(path.relative(config.root, file))
})
} else {
// loaded but not in the module graph, probably not js
debugHmr(`[no modules matched] ${chalk.dim(shortFile)}`)
}
Vue 等文件更新时,都会进入 updateModules 方法,正常情况下只会触发 update,实现热更新,热替换;function updateModules(
file: string,
modules: ModuleNode[],
timestamp: number,
{ config, ws }: ViteDevServer
) {
const updates: Update[] = []
const invalidatedModules = new Set<ModuleNode>()
// 遍历插件数组,关联下面的片段
for (const mod of modules) {
const boundaries = new Set<{
boundary: ModuleNode
acceptedVia: ModuleNode
}>()
// 设置时间戳
invalidate(mod, timestamp, invalidatedModules)
// 查找引用模块,判断是否需要重载页面
const hasDeadEnd = propagateUpdate(mod, timestamp, boundaries)
// 找不到引用者则会发起刷新
if (hasDeadEnd) {
config.logger.info(chalk.green(`page reload `) + chalk.dim(file), {
clear: true,
timestamp: true
})
ws.send({
type: 'full-reload'
})
return
}
updates.push(
...[...boundaries].map(({ boundary, acceptedVia }) => ({
type: `${boundary.type}-update` as Update['type'],
timestamp,
path: boundary.url,
acceptedPath: acceptedVia.url
}))
)
}
// 堆代码 duidaima.com
// 日志输出
config.logger.info(
updates
.map(({ path }) => chalk.green(`hmr update `) + chalk.dim(path))
.join('\n'),
{ clear: true, timestamp: true }
)
// 向客户端发送消息,进行热更新操作
ws.send({
type: 'update',
updates
})
}
上面代码中的 modules 是热更新时需要执行的各个插件for (const plugin of config.plugins) {
if (plugin.handleHotUpdate) {
const filteredModules = await plugin.handleHotUpdate(hmrContext)
if (filteredModules) {
hmrContext.modules = filteredModules
}
}
}
Vite 会把模块的依赖关系组合成 moduleGraph,它的结构类似树形,热更新中判断哪些文件需要更新也会依赖 moduleGraph;它的文件内容大致如下:// moduleGraph 返回的 ModuleNode 大致结构
ModuleNode {
id: '/Users/zcy/Documents/workspace/back-sky-front/src/pages/back-sky/index.js',
file: '/Users/zcy/Documents/workspace/back-sky-front/src/pages/back-sky/index.js',
importers: Set {},
importedModules: Set {
ModuleNode {
id: '/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/vue.js?v=32cfd30c',
file: '/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/vue.js',
......
lastHMRTimestamp: 0,
url: '/node_modules/.vite/vue.js?v=32cfd30c',
type: 'js'
},
ModuleNode {
id: '/Users/zcy/Documents/workspace/back-sky-front/src/pages/back-sky/index.vue',
file: '/Users/zcy/Documents/workspace/back-sky-front/src/pages/back-sky/index.vue',
......
url: '/src/pages/back-sky/index.vue',
type: 'js'
},
ModuleNode {
id: '/Users/zcy/Documents/workspace/back-sky-front/node_modules/element-plus/lib/theme-chalk/index.css',
file: '/Users/zcy/Documents/workspace/back-sky-front/node_modules/element-plus/lib/theme-chalk/index.css',
importers: [Set],
importedModules: Set {},
acceptedHmrDeps: Set {},
isSelfAccepting: true,
transformResult: [Object],
ssrTransformResult: null,
ssrModule: null,
lastHMRTimestamp: 0,
url: '/node_modules/element-plus/lib/theme-chalk/index.css',
type: 'js'
},
......
},
acceptedHmrDeps: Set {},
isSelfAccepting: false,
transformResult: {
code: 'import __vite__cjsImport0_vue from ' +
'"/node_modules/.vite/vue.js?v=32cfd30c"; const createApp = ' +
'__vite__cjsImport0_vue["createApp"];\nimport App from ' +
"'/src/pages/back-sky/index.vue';\nimport " +
"'/node_modules/element-plus/lib/theme-chalk/index.css';\n\nconst app = " +
'createApp(App);\n\nimport { addHistoryMethod } from ' +
"'/src/pages/back-sky/api/index.js';\nimport {\n ElButton,\n ElDropdown,\n " +
'ElDropdownMenu,\n ElDropdownItem,\n ElMenu,\n ElSubmenu,\n ElMenuItem,\n ' +
'ElMenuItemGroup,\n ElPopover,\n ElDialog,\n ElRow,\n ElInput,\n ' +
"ElLoading,\n} from '/node_modules/.vite/element-plus.js?v=32cfd30c';\n\n" +
'app.use(ElButton);\napp.use(ElLoading);\napp.use(ElDropdown);\n' +
'app.use(ElDropdownMenu);\napp.use(ElDropdownItem);\napp.use(ElMenu);\n' +
'app.use(ElSubmenu);\napp.use(ElMenuItem);\napp.use(ElMenuItemGroup);\n' +
'app.use(ElPopover);\napp.use(ElDialog);\napp.use(ElRow);\napp.use(ElInput);\n' +
"\nconst f = ()=>{\n return app.mount('#app');\n};\n\nconst $backsky = " +
"document.getElementById('back-sky');\nif($backsky) {\n $backsky.innerHTML " +
"= '';\n $backsky.appendChild(f().$el);\n} else {\n window.onload = " +
"function(){\n document.getElementById('back-sky') && " +
"document.getElementById('back-sky').appendChild(f().$el);\n };\n}\n\n" +
"window.addHistoryListener = addHistoryMethod('historychange');\n" +
"history.pushState = addHistoryMethod('pushState');\nhistory.replaceState " +
"= addHistoryMethod('replaceState');\n\n// 监听hash路由变化,不与onhashchange互相覆盖\n" +
'addHashChange(()=>{\n setTimeout(() => {\n const $backsky = ' +
"document.getElementById('back-sky');\n if($backsky && " +
"$backsky.innerHTML === '') {\n $backsky.appendChild(f().$el);\n }\n " +
" },0);\n});\n\nfunction addHashChange(callback) {\n if('onhashchange' in " +
'window === false){//浏览器不支持\n return false;\n }\n ' +
'if(window.addEventListener) {\n ' +
"window.addEventListener('hashchange',function(e) {\n callback && " +
'callback(e);\n },false);\n }else if(window.attachEvent) {//IE 8 及更早 IE ' +
"版本浏览器\n window.attachEvent('onhashchange',function(e) {\n callback " +
'&& callback(e);\n });\n }\n ' +
"window.addHistoryListener('history',function(e){\n callback && " +
'callback(e);\n });\n}\n\n\n',
map: null,
etag: 'W/"846-Qa424gJKl3YCqHDWXXsM1mFHRqg"'
},
ssrTransformResult: null,
ssrModule: null,
lastHMRTimestamp: 0,
url: '/src/pages/back-sky/index.js',
type: 'js'
}
npm install vue@next并为项目添加 vite 配置文件,在根目录下创建 vite.config.js,并为它添加一些基础的配置。
// vite.config.js
// vite2.1.5
const path = require('path');
import vue from '@vitejs/plugin-vue';
export default {
// 配置选项
resolve: {
alias: {
'@utils': path.resolve(__dirname, './src/utils')
},
},
plugins: [vue()],
};
引用的第三方组件库可能也会需要升级,例如:升 element-ui 至 element-plusnpm install element-plusVue3 在 import 时,需使用 createApp 方法进行初始化
import { createApp } from 'vue';
import App from './index.vue';
const app = createApp(App);
import {
ElInput,
ElLoading,
} from 'element-plus';
app.use(ElButton);
app.use(ElLoading);
......
到这里就可以将项目运行起来了。 注意:Vite 官方不允许省略 .vue 后缀,否则就会报错;[plugin:vite:import-analysis] Failed to resolve import "./todoList" from "src/pages/back-sky/components/header/index.vue". Does the file exist? /components/header/index.vue:2:23 1 | 2 | import todoList from './todoList'; import todoList from './todoList.vue';最后我们来对比一下该项目两种构建方式时间的对比;
⚠ 「wdm」: Hash: 1ad1dd54289cfad8ecbe Version: webpack 4.46.0 Time: 7513ms Built at: 2021-05-24 13:59:35相同项目 Vite 冷启动,耗时 924ms:
> vite Pre-bundling dependencies: vue element-plus @zcy/zcy-request element-plus/lib/utils/dom (this will be run only when your dependencies or config have changed) vite v2.3.3 dev server running at: > Local: http://localhost:3000/ > Network: use `--host` to expose ready in 924ms.二次启动(预编译的依赖已存在),耗时 407ms;
> vite vite v2.3.3 dev server running at: > Local: http://localhost:3000/ > Network: use `--host` to expose ready in 407ms.