一文带你了解vite对浏览器的请求做了什么

2022-04-15 0 349
目录
  • 工作原理:
  • 浏览器做的什么事啊
    • 宿主文件index.html
    • main.js
    • 其他裸模块
  • 了解一下预打包
    • 服务器做的什么事啊
      • 请求首页index.html
      • 请求以.js结尾的文件
        • 基础js文件
        • 对main中的依赖进行处理
        • 处理.vue文件
      • 处理图片路径
      • 总结

        工作原理:

        • type=”module” 浏览器中ES Module原生native支持。 如果浏览器支持type=”module” ,我i们可以使用es6模块化的方式编写。浏览器会把我们需要导入的文件再发一次http请求,再发到服务器上。 开发阶段不需要打包
        • 第三方依赖预打包
        • 启动一个开发服务器处理资源请求

        一图详解vite原理:

        一文带你了解vite对浏览器的请求做了什么

        浏览器做的什么事啊

        宿主文件index.html

        <script type="module" src="/src/main.js"></script>
        

        浏览器获取到宿主文件中的资源后,发现还要再去请求main.js文件。会再向服务端发送一次main.js的资源请求。

        一文带你了解vite对浏览器的请求做了什么

        main.js

        在main中,可以发现,浏览器又再次发起对vue.js?v=d253a66c、App.vue?t=1637479953836两个文件的资源请求。

        服务器会将App.vue中的内容进行编译然后返回给浏览器,下图可以看出logo图片和文字都被编译成_hoisted_ 的静态节点。

        一文带你了解vite对浏览器的请求做了什么

        从请求头中,也可以看出sfc文件已经变成浏览器可以识别的js文件(app.vue文件中要存在script内容才会编译成js)。对于浏览器来说,执行的就是一段js代码。

        一文带你了解vite对浏览器的请求做了什么

        其他裸模块

        如果vue依赖中还存在其他依赖的话,浏览器依旧会再次发起资源请求,获取相应资源。

        了解一下预打包

        对于第三方依赖(裸模块)的加载,vite对其提前做好打包工作,将其放到node_modules/.vite下。当启动项目的时候,直接从该路径下下载文件。

        一文带你了解vite对浏览器的请求做了什么

        通过上图,可以看到再裸模块的引入时,路径发生了改变。

        服务器做的什么事啊

        总结一句话:服务器把特殊后缀名的文件进行处理返回给前端展示。

        我们可以模拟vite的devServe,使用koa中间件启动一个本地服务。

        // 引入依赖
        const Koa = require('koa')
        const app = new Koa()
        const fs = require('fs')
        const path = require('path')
        const compilerSfc = require('@vue/compiler-sfc')
        const compilerDom = require('@vue/compiler-dom')
        
        app.use(async (ctx) => {
         const { url, query } = ctx.request
         // 处理请求资源代码都写这
        })
        app.listen(3001, () => {
          console.log('dyVite start!!')
        })
        

        请求首页index.html

         if (url === '/') {
            const p = path.join(__dirname, './index.html') // 绝对路径
            //  首页
            ctx.type = 'text/html'
            ctx.body = fs.readFileSync(p, 'utf8')
          }
        

        一文带你了解vite对浏览器的请求做了什么

        看到上面这张图,就知道我们的宿主文件已经请求成功了。只是浏览器又给服务端发送的一个main.js文件的请求。这时,我们还需要判断处理一下main.js文件。

        请求以.js结尾的文件

        我们处理上述情况后,emmmm。。。发现main中还是存在好多其他资源请求。

        基础js文件

        main文件:

        console.log(1)
        

        处理main:

        else if (url.endsWith('.js')) {
            // 响应js请求
            const p = path.join(__dirname, url)
            ctx.type = 'text/javascript'
            ctx.body = rewriteImport(fs.readFileSync(p, 'utf8')) // 处理依赖函数
          }
        

        对main中的依赖进行处理

        你以为main里面就一个输出吗?太天真了。这样的还能处理吗?

        main文件:

        import { createApp, h } from 'vue'
        createApp({ render: () => h('div', 'helllo dyVite!') }).mount('#app')
        

        emmm。。。应该可以!

        我们可以将main中导入的地址变成相对地址。

        在裸模块路径添加上/@modules/。再去识别/@modules/的文件即(裸模块文件)。

        // 把能读出来的文件地址变成相对地址
        // 正则替换 重写导入 变成相对地址
        // import { createApp } from 'vue'  => import { createApp } from '/@modules/vue'
        function rewriteImport(content) {
          return content.replace(/ from ['|"](.*)['|"]/g, function (s0, s1) {
            //  s0匹配字符串,s1分组内容
            // 是否是相对路径
            if (s1.startsWith('./') || s1.startsWith('/') || s1.startsWith('../')) {
              // 直接返回
              return s0
            } else {
              return ` from '/@modules/${s1}'`
            }
          })
        }
        

        对于第三方依赖,vite内部是使用预打包请求自己服务器/node_modules/.vite/下的内部资源。

        我们可以简单化一点,将拿到的依赖名去客户端下的node_modules下拿相应的资源。

          else if (url.startsWith('/@modules/')) {
            // 裸模块的加载
            const moduleName = url.replace('/@modules/', '')
            const pre![1637477009328](imgs/1637477009328.png)![1637477009368](imgs/1637477009368.png)的地址
            const module = require(prefix + '/package.json').module
            const filePath = path.join(prefix, module) // 拿到文件加载的地址
            // 读取相关依赖
            const ret = fs.readFileSync(filePath, 'utf8')
            ctx.type = 'text/javascript'
            ctx.body = rewriteImport(ret) //依赖内部可能还存在依赖,需要递归
          }
        

        在main中进行render时,会报下图错误:

        一文带你了解vite对浏览器的请求做了什么

        我们加载的文件都是服务端执行的库,内部可能会产生node环境的代码,需要判断一下环境变量。如果开发时,会输出一些警告信息,但是在前端是没有的。所以我们需要mock一下,告诉浏览器我们当前的环境。

        给html加上process环境变量。

          <script>
            window.process = { env: { NODE_ENV: 'dev' } }
          </script>
        

        此时main文件算是加载出来了。

        但是这远远打不到我们的目的啊!

        我们需要的是可以编译vue文件的服务器啊!

        处理.vue文件

        main.js文件:

        import { createApp, h } from 'vue'
        import App from './App.vue'
        createApp(App).mount('#app')
        

        在vue文件中,它是模块化加载的。

        一文带你了解vite对浏览器的请求做了什么

        我们需要在处理vue文件的时候,对.vue后面携带的参数做处理。

        在此,我们简化只考虑template和sfc情况。

        else if (url.indexOf('.vue') > -1) {
            // 处理vue文件  App.vue?vue&type=style&index=0&lang.css
            // 读取vue内容
            const p = path.join(__dirname, url.split('?')[0])
            // compilerSfc解析sfc  获得ast
            const ret = compilerSfc.parse(fs.readFileSync(p, 'utf8'))
            // App.vue?type=template
            // 如果请求没有query.type 说明是sfc
            if (!query.type) {
              // 处理内部的script
              const scriptContent = ret.descriptor.script.content
              // 将默认导出配置对象转为常量
              const script = scriptContent.replace(
                'export default ',
                'const __script = ',
              )
              ctx.type = 'text/javascript'
              ctx.body = `
          ${rewriteImport(script)}
          // template解析转换为单独请求一个资源
          import {render as __render} from '${url}?type=template'
          __script.render = __render
          export default __script
        `
            } else if (query.type === 'template') {
              const tpl = ret.descriptor.template.content
              // 编译包含render模块
              const render = compilerDom.compile(tpl, { mode: 'module' }).code
              ctx.type = 'text/javascript'
              ctx.body = rewriteImport(render)
            }
          }
        

        处理图片路径

        直接从客户端读取返回。

         else if (url.endsWith('.png')) {
            ctx.body = fs.readFileSync('src' + url)
          }

        总结

        到此这篇关于vite对浏览器的请求做了什么的文章就介绍到这了,更多相关vite浏览器的请求内容请搜索NICE源码以前的文章或继续浏览下面的相关文章希望大家以后多多支持NICE源码!

        免责声明:
        1、本网站所有发布的源码、软件和资料均为收集各大资源网站整理而来;仅限用于学习和研究目的,您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。 不得使用于非法商业用途,不得违反国家法律。否则后果自负!

        2、本站信息来自网络,版权争议与本站无关。一切关于该资源商业行为与www.niceym.com无关。
        如果您喜欢该程序,请支持正版源码、软件,购买注册,得到更好的正版服务。
        如有侵犯你版权的,请邮件与我们联系处理(邮箱:skknet@qq.com),本站将立即改正。

        NICE源码网 JavaScript 一文带你了解vite对浏览器的请求做了什么 https://www.niceym.com/20835.html