最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vite 实现原理

    正文概述 掘金(不梨)   2020-12-16   651

    Vite 实现原理

    1)Vite 概念

    • Vite 是一个面向现代浏览器的一个更轻、更快的 Web 应用开发工具
    • 它基于 ECMAScript 标准原生模块系统 (ES Module) 实现

    2)对比 vue-cli-service serve

    • vite serve

    Vite 实现原理

    • vue-cli-service serve

    Vite 实现原理

    3)HMR

    • Vite HMR
      • 立即编译当前所修改的文件
    • Webpack HMR
      • 会自动以这个文件为入口重写 build 一次,所有的涉及到的依赖也都会被加载一遍

    4)Vite 特性

    • 快速冷启动
    • 模块热更新
    • 按需编译
    • 开箱即用

    5)模拟实现

    这里只实现了 静态 web 服务器加载第三方模块编译单文件组件(.vue) ,没有处理 css等文件

    #!/usr/bin/env node
    
    const Koa = require('koa')
    const send = require('koa-send')
    const path = require('path')
    const compilerSFC = require('@vue/compiler-sfc')
    const { Readable } = require('stream')
    
    const app = new Koa()
    
    // 流转换为字符串
    const streamToString = stream => new Promise((resolve, reject) => {
        const chunks = []
        stream.on('data', chunk => chunks.push(chunk))
        stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')))
        stream.on('error', reject)
      })
    
    // 字符串转换为流
    const stringToStream = text => {
        const stream = new Readable()
        stream.push(text)
        stream.push(null)
        return stream
      }
    
    // 3. 加载第三方模块
    app.use(async (ctx,next) => {
        // ctx.path --> /@modules/vue
        if (ctx.path.startsWith('/@modules/')) {
            const moduleName = ctx.path.substr(10)
            // console.log("? ~ file: index.js ~ line 22 ~ app.use ~ moduleName", moduleName)
    
            const pkgPath = path.join(process.cwd(), 'node_modules', moduleName, 'package.json')
            // console.log("? ~ file: index.js ~ line 24 ~ app.use ~ pkgPath", pkgPath)
    
            const pkg = require(pkgPath)
            // console.log("? ~ file: index.js ~ line 26 ~ app.use ~ pkg", pkg)
    
            ctx.path = path.join('/node_modules', moduleName, pkg.module)
            // console.log("? ~ file: index.js ~ line 28 ~ app.use ~ ctx.path", ctx.path)
        }
        await next()
    })
    
    // 1. 静态文件服务器
    app.use(async (ctx, next) => {
        console.log('ctx.path----', ctx.path)
        
        await send(ctx, ctx.path, {root: process.cwd(), index: 'index.html'})
        await next()
    })
    
    // 4. 处理单文件组件
    app.use(async (ctx, next) => {
        if (ctx.path.endsWith('.vue')) {
            const contents = await streamToString(ctx.body)
            // console.log("? ~ file: index.js ~ line 58 ~ app.use ~ contents", contents)
            // console.log('compilerSFC.parse(contents)----', compilerSFC.parse(contents))
            const { descriptor } = compilerSFC.parse(contents)
            let code
            if (!ctx.query.type) {
                // 单文件组件第一次请求
                // 单文件组件编译成选项对象,返回给浏览器
                code = descriptor.script.content
                // console.log(code)
                code = code.replace(/export\s+default\s+/g, 'const __script = ')
                // console.log("? ~ file: index.js ~ line 66 ~ app.use ~ code", code)
                code += `
                    import { render as __render } from "${ctx.path}?type=template"
                    __script.render = __render
                    export default __script
                `
            } else if (ctx.query.type === 'template') {
                // 单文件组件第二次请求
                // 服务器编译模板,返回编译之后的 render 函数
                const templateRender = compilerSFC.compileTemplate({ source: descriptor.template.content })
                console.log("? ~ file: index.js ~ line 77 ~ app.use ~ templateRender", templateRender)
                code = templateRender.code
            }
    
            ctx.type = 'application/javascript'
            ctx.body = stringToStream(code)
        }
    
        await next()
    })
    
    // 2. 修改第三方模块的路径
    app.use(async (ctx, next) => {
        if (ctx.type === 'application/javascript') {
            const contents = await streamToString(ctx.body)
            // import vue from 'vue'
            // import App from './App.vue'
            ctx.body = contents
                .replace(/(from\s+['"])(?![\.\/])/g, '$1/@modules/')
                .replace(/process\.env\.NODE_ENV/g, '"development"')
        }
    })
    
    app.listen(3000)
    console.log('Server runing @ 3000')
    

    起源地下载网 » Vite 实现原理

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元