前端新工具–vite从入门到实战(一)

2022-12-05 0 976

前几日

B站现场直播,如是说了这款捷伊后端应用软件,借助了应用程序便携式的import监督机制,不论数不清的工程项目,都是秒开,听出来很吸睛,迅即看了源代码,因此前几日做了《后端会客室》后,历经孔布龙特地传授了结构设计路子,又有了新体悟,写个该文归纳下列

能和孔布龙私下沟通交流vue3的结构设计路子 斩获吗非常大,前几日也正式成为了vue3的contributor,期望三季度能给vue自然生态重大贡献更多的标识符

前端新工具–vite从入门到实战(一)

#TOC

进阶采用他们同时实现全力支持html全力支持js全力支持服务器端组件全力支持.vue组件全力支持import css下集

补足

vite合作开发自然环境借助应用程序的import监督机制,装箱右内建的rollup,因此早已能间接用了

两栖作战

那个没啥,githubTG106把,贼单纯https://github.com/vitejs/vite

$ npm init vite-app <project-name> $ cd <project-name> $ npm install $ npm run dev

基本原理

接着他们瞧瞧约莫的标识符 不遗余力的简化

➜ vite-app tree . ├── index.html ├── package.json ├── public │ └── favicon.ico └── src ├── App.vue ├── assets │ └── logo.png ├── components │ └── HelloWorld.vue ├── index.css └── main.js

瞧瞧index和main, 是借助了应用程序便携式的import监督机制,

<!DOCTYPE html> <html lang=“en”> <head> <meta charset=“UTF-8”> <link rel=“icon” href=“/favicon.ico” /> <title>Vite App</title> </head> <body> <div id=“app”></div> <script type=“module” src=“/src/main.js”></script> </body> </html> import { createApp } from vue import App from ./App.vue import ./index.css createApp(App).mount(#app)

当应用程序识别type=”module”引

import {log} from ./util.js log(xx)

目录新建util.js

export function log(msg){ console.log(msg) }

但是现在会有一个小报错

Access to script at file:///src/main.js from origin null has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https. main.js:1 Failed to load resource: net::ERR_FAILED /favicon.ico:1 Failed to load resource: net::ERR_FILE_NOT_FOUND

vite的任务,是用koa起一个http 服务,来拦截这些请求,返回合适的结果,就欧克了,下面他们一步步来,为了方便演示,标识符单纯粗暴

全力支持html和js

先不废话了,他们先用朴实无话的if else试下那个demo的功能

npm install koa –save

拦截路由/ 和xx.js结尾的请求,标识符呼之欲出

const fs = require(fs) const path = require(path) const Koa = require(koa) const app = new Koa() app.use(async ctx=>{ const {request:{url} } = ctx // 首页 if(url==/){n ctx.type=“text/html” ctx.body = fs.readFileSync(./index.html,utf-8) }else if(url.endsWith(.js)){ // js文件 const p = path.resolve(__dirname,url.slice(1)) ctx.type = application/javascript const content = fs.readFileSync(p,utf-8) ctx.body = content } }) app.listen(3001, ()=>{ console.log(听我口令,3001端口,起~~) })

访问locaohost:3001 瞧瞧console和network 搞定第一步 全力支持了import 本底的js文件

前端新工具–vite从入门到实战(一)
前端新工具–vite从入门到实战(一)

看到这里,你应该约莫对vite为什么快,有一个初步的认识,这是天生的按需加载呀,告别冗长的webpack装箱

服务器端库

他们不能满足于此,毕竟不可能所有组件都他们写,比如我们用到的vue 是从npm 引入的,准确的来说,是从node_module引入的 改一下main.js

import { createApp } from vue console.log(createApp)

不出意外 报错了 他们要解决两个问题

1. 不是合法的相对路径,应用程序报错

Uncaught TypeError: Failed to resolve module specifier “vue”. Relative references must start with either “/”, “./”, or “../”.

约莫意思是”/”, “./”, or “../”开头的路径,才是合法的,那个其实也好说,他们对main.js里返回的内容做个重写就能,他们做个规定,把import from 后面,不是上面仨符号开头的,加一个/@module/前缀

// 替换前 import { createApp } from vue // 替换后 import { createApp } from /@module/vue

他们新建一个函数,其实vite是用的es-module-lexer来解析成ast拿到import的地址,他们既然是乞丐版,整个土鳖的正则把

// 单引号双引号都全力支持 我真是个小机灵 /from [“]([^”]+)[“]/g
前端新工具–vite从入门到实战(一)

约莫是from 后面 引号中间的内容抠出来 验证下列看看是不是加前缀即可,路子明确,标识符就呼之欲出了

function rewriteImport(content){ return content.replace(/from [“]([^”]+)[“]/g, function(s0,s1){ // . ../ /开头的,都是相对路径 if(s1[0]!==.&& s1[1]!==/){ return `from /@modules/${s1}` }else{ return s0 } }) } if(url.endsWith(.js)){ // js文件 const p = path.resolve(__dirname,url.slice(1)) ctx.type = application/javascript const content = fs.readFileSync(p,utf-8) ctx.body = rewriteImport(content) }

在刷新,报了另外一个错 说明组件重写完毕,下面他们需要全力支持@module的前缀

GET http://localhost:3001/@modules/vue net::ERR_ABORTED 404 (Not Found)

全力支持/@module/

解析的url的时候,加一个判断即可,主要是要去node_module里找 约莫逻辑

url开头是/@module/ 就把剩下的路径扣下来去node_module里找到那个库,把package.json读出来他们用的import语法,因此把package.json里的Module字段读出来,是工程项目的入口 替换回来即可
路子清楚了,标识符就呼之欲出了 —- 孟德鸠斯

注意node_module里的文件,也是有import 别的npm 包的,因此记得返回也要用rewriteImport包下列

if(url.startsWith(/@modules/)){ // 这是一个node_module里的东西 const prefix = path.resolve(__dirname,node_modules,url.replace(/@modules/,)) const module = require(prefix+/package.json).module const p = path.resolve(prefix,module) const ret = fs.readFileSync(p,utf-8) ctx.type = application/javascript ctx.body = rewriteImport(ret) }

接着报了一个小错 是vue源代码里有用process.ENV判断自然环境的,他们应用程序client里设置下列即可

Uncaught ReferenceError: process is not defined at shared:442

他们注入一个全局变量 ,vite的做法是解析html之后,通过plugin的方式注入,逼格很高,我这乞丐版,凑和replace一下把

if(url==/){ ctx.type=“text/html” let content = fs.readFileSync(./index.html,utf-8) content = content.replace(<script ,` <script> window.process = {env:{ NODE_ENV:dev}} </script> <script `) ctx.body = content }

打开console yeah 折腾了半天,终于全力支持了第一行

前端新工具–vite从入门到实战(一)

.vue组件

接着他们把标识符补全 main.js

import { createApp } from vue // node_moduleimport App from ./App.vue // import ./index.css createApp(App).mount(#app)

App.vue

<template> <h1>大家好 kkb欢迎你</h1> <h2> <span>count is {{count}}</span> <button @click=“count++”>戳我</button> </h2> </template> <script> import {ref,computed} from vue export default { setup(){ const count = ref(0) function add(){ count.value++ } const double = computed(()=>count.value*2) return {count,add,double} } } </script>

ok不出所料的报错了 毕竟他们node环境还没全力支持单文件组件,大家其实瞧瞧vite工程项目的network就约莫知道基本原理了

发起.vue的请求后,先把script解析出来,接着里面加上请求template和css的import语句把template解析成render函数,返回拼成一个组件还是那句话,路子通了,标识符就呼之欲出了,当时看到这里,觉得孔布龙真是优秀啊
前端新工具–vite从入门到实战(一)
前端新工具–vite从入门到实战(一)

看到app.vue的返回结果没,这是他们的目标,核心是

const __script = { setup() { } } import {render as __render} from “/src/App.vue?type=template&t=1592389791757” __script.render = __render export default __script

好了 写标识符 拼呗

单文件组件解析

他们就不考虑缓存了,间接解析,他们间接用vue官方的@vue/compiler-sfc来整单文件,用@vue/compiler-dom来把template解析成 ,这块核心逻辑都是这里vue核心包的,他们反而没做啥,路子通了写标识符

if(url.indexOf(.vue)>-1){ // vue单文件组件 const p = path.resolve(__dirname, url.split(?)[0].slice(1)) const {descriptor} = compilerSfc.parse(fs.readFileSync(p,utf-8)) if(!query.type){ ctx.type = application/javascript // 借用vue自导的compile框架 解析单文件组件,其实相当于vue-loader做的事情 ctx.body = ` // option组件 ${rewriteImport(descriptor.script.content.replace(export default ,const __script =))} import { render as __render } from “${url}?type=template” __script.render = __render export default __script ` } }

瞧瞧结果 完美下一步搞定type=template的解析就能,

前端新工具–vite从入门到实战(一)

模板解析

间接@vue/compiler-dom把html解析城render就能, 能线体验一波

if(request.query.type===template){ // 模板内容 const template = descriptor.template // 要在server端吧compiler做了 const render = compilerDom.compile(template.content, {mode:“module”}).code ctx.type = application/javascript ctx.body = rewriteImport(render) }

体验一下

前端新工具–vite从入门到实战(一)

全力支持css

其他的就路子类似了 比如全力支持css

import { createApp } from vue // node_module import App from ./App.vue // 解析成额外的 ?type=template请求 import ./index.css createApp(App).mount(#app)

标识符间接呼

if(url.endsWith(.css)){ const p = path.resolve(__dirname,url.slice(1)) const file = fs.readFileSync(p,utf-8) const content = `const css = “${file.replace(/\n/g,)} let link = document.createElement(style) link.setAttribute(type, text/css) document.head.appendChild(link) link.innerHTML = css export default css ` ctx.type = application/javascript ctx.body = content }

其实内部设置css的逻辑,应该再client端注入,最好每个link加一个id,方便后续做热更新

前端新工具–vite从入门到实战(一)

全力支持typescript

其实全力支持less啥的逻辑都是类似的,vite用了esbuild来解析typescript, 比官方的tsc快了几十倍,快去体验一波vite的同时实现ifelse太多了,不不献丑了,下次在写 其实全力支持less sass都是类似的逻辑

归纳

以上逻辑其实大家间接去看vite的import解析源代码更合适 ,我只是期望能讲明白路子 标识符略丑 请轻喷 是通过拦截import的http请求,来同时实现无需装箱,便携式按需加载的工具

下一次来讲一下热更新怎么做的,其实核心逻辑是注入http://socket.io ,后端数据变了,通知后端即可,约莫类型如下 在线标识符

// 不同的更新方式 interface HMRPayload { type: | js-update | vue-reload | vue-rerender | style-update | style-remove | full-reload | sw-bust-cache | custom timestamp: number path?: string changeSrcPath?: string id?: string index?: number customData?: any }

client标识符

switch (type) { case vue-reload: Vue组件更新 case vue-rerender: Vue-template更新 case style-update: css更新 case style-remove: css删除 case js-update: js更新 case full-reload: 全量重载更新

到此为止基本上vite他们就进阶了,下篇该文写一下如何做的热更新 欢迎关注 ,敬请期待

标识符地址

https://github.com/shengxinjing/vue3-vs-vue2下面的vite-mini文件夹 其实那个标识符仓库是他们开课吧搞得一个节目《后端会客室》,由我、winter还有孔布龙搞得一次聊vue的现场标识符

正在剪辑中,欢迎关注我,视频出来我尽快发出来

下集

也欢迎关注公众号 嘿嘿 一起摸鱼

前端新工具–vite从入门到实战(一)

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务