搞懂vite

vite

vite讲解

Vite是一个由原生ES module驱动的web开发构建工具,在开发环境下基于浏览器原生Es import开发,在生产环境下基于Rollup打包。

vite的特点有:

  • 闪电般的冷启动速度
  • 即使热更新
  • 真正的按需编译

要求:

vite 要求项目完全由esmodule模块组成
common.js模块不能在vite上使用
打包上依旧还是rollup等传统打包工具

vite的底层实现:

  1. 已知目录结构在这里插入图片描述
  2. App.vue内容
<template>
  <div>
    <h1>大家好,我是阿亮</h1>
    <h2>
      <span>count is {{ count }}</span>
      <button @click="count++">+1</button>
      <button @click="add">add+1</button>
    </h2>
  </div>
</template>

<script>
import { ref } from "vue";
export default {
  setup() {
    const count = ref(6);
    function add() {
      count.value++;
    }
    return { add, count };
  },
};
</script>
  1. index.css
h1 {color: red;}
  1. main.js
import { str } from './moduleA.js'
console.log('vite ...', str);
import { createApp, h } from 'vue';
import App from './App.vue'
import './index.css'
const App1 = {
    render() {
        // <div><div>hello vite</div></div>
        return h('div', null, [
            h('div', null, String('hello vite'))
        ])
    }
}
createApp(App).mount('#app')
  1. moduleA.js
export const str = 'zanlan 321'
  1. index.html
<h1>hello vite</h1>
<div id="app"></div>
<script src="./src/main.js" type="module"></script>
  1. index.js

sfc (single-file-components)

const Koa = require('Koa')

const fs = require('fs')
const path = require('path')
const app = new Koa()
const compilerSfc = require('@vue/compiler-sfc')
const compilerDom = require('@vue/compiler-dom')

app.use(async (ctx) => {
    const { url, query } = ctx.request
    console.log('url ------------' + url);
    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
    } else if (url.endsWith('.js')) {
        //  /src/main.js => 代码文件所在位置/src/main.js
        const p = path.resolve(__dirname, url.slice(1))
        const content = fs.readFileSync(p, 'utf-8')
        ctx.type = 'application/javascript'
        ctx.body = rewriteImport(content)
    } else if (url.startsWith('/@modules')) {
        // 第三方库的支持
        // /@modules/vue => node_modules
        // /@modules/vue => 代码的位置/node_modules/vue/ 的es模块入口
        const prefix = path.resolve(
            __dirname,
            "node_modules",
            url.replace("/@modules/", "")
        )
        // 读取package.json的module属性
        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)
    } else if (url.indexOf('.vue') > -1) {
        // /*.vue?type = template
        const p = path.resolve(__dirname, url.split('?')[0].slice(1))
        const { descriptor } = compilerSfc.parse(fs.readFileSync(p, 'utf-8'))
        if (!query.type) {
            // 第一步vue文件 => template script (compiler-sfc)
            // descriptor => js + template生成render部分
            ctx.type = "application/javascript"
            // 借用vue自导的compile框架,解析单文件组件,其实相当于vue-loader做的事情
            ctx.body = `
            ${rewriteImport(
                descriptor.script.content.replace("export default", "const __script = ")
            )}
            import { render as __render } from "${url}?type=template"
            __script.render = __render
            export default __script
            `
        } else {
            // 第二步 template模板 => render函数(compiler-dom)
            const template = descriptor.template
            const render = compilerDom.compile(template.content, { mode: 'module' })
            ctx.type = "application/javascript"
            console.log('render', render);
            ctx.body = rewriteImport(render.code)
        }
    } else if (url.endsWith('.css')) {
        console.log('9999999999999999999999999');
        const p = path.resolve(__dirname, url.slice(1))
        const file = fs.readFileSync(p, 'utf-8')
        // css转换为js代码
        // 利用js添加一个style标签
        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
    }



    // 第三方库的支持
    // vue => node_modules/***
    // 改写函数
    // 需要改写 欺骗一下浏览器 'vue' => '/@modules/vue' => 别名
    // from 'xxx'
    function rewriteImport(content) {
        return content.replace(/ from ['|"]([^'"]+)['|""]/g, (...args) => {
            if (args[1][0] !== '.' && args[1][1] !== '/') {
                return ` from '/@modules/${args[1]}'`
            } else {
                return args[0]
            }
        })
    }
    /**
    rewriteImport('import { createApp, h } from \'vue\'')
	"import { createApp, h } from '/@modules/vue'"
*/

})

app.listen(3000, () => {
    console.log('vite start at 3000');
})
  1. pakage.json
{
  "name": "vite",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "koa": "^2.13.4",
    "vue": "^3.2.36"
  }
}
  1. 执行node index.js
  2. 在页面上输入 localhost:3000,展示页面如下:在这里插入图片描述

版权声明:本文为formylovetm原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。