• 从0到1手把手实现vite


    什么是Vite?

    • 法语:轻量化,快速
    • 基于VUE3 非 打包开发服务器,请注意,它是个开发服务器哇!!
    • 快速开发,按需编译,不再等待整个应用编译完成
    • 基于原生模块系统ESModule实现
    • 说白了,就是一个node.js服务器,帮你运行起来代码,让你可以调试
    • 它和我们部署的webserver不同的是,里面内置了websoket,当监听到代码改变 通过websoket更新代码,实现热更新

    实现原理

    • 使用export import 方式导入导出模块,同时实现按需加载
    • 高度依赖module script 特性

    下面开始搞事情吧~~

    • 兼容性注意
    • Vite 需要 Node.js 版本 >= 12.0.0。然而,有些模板需要依赖更高的 Node 版本才能正常运行,当你的包管理器发出警告时,请注意升级你的 Node 版本。

    新建一个文件夹,执行命令,构建一个 Vite + Vue 项目

    # npm 6.x
    npm create vite@latest my-vue-app --template vue
    
    # npm 7+, extra double-dash is needed:
    npm create vite@latest my-vue-app -- --template vue
    

    进入项目,执行

     cd my-vue-app
     npm install
     npm run dev
    

    恭喜哇,项目跑起来啦~

    • 复制地址用浏览器访问

    打开项目看一下我们的目录

    看个厉害的e

    是不是很好奇,为什么会可以直接加载.vue文件呢???接下来我们就开始探索一下吧

    • 新建一个文件夹 vite,然后进去执行下面命令
    npm init -y
    
    • 然后生成了一个package.json文件

    • 安装依赖 koa 用来实现node服务器,它帮我们封装了node-http哦
    npm install koa
    

    • 接下来我们看一下,当我们执行 npm run dev 的时候,实际是执行了下面的命令,如果我们想要实现这个命令,我们又该怎么做呢?

    新建命令执行入口 vite\bin\www.js

    配置package.json

    "bin":"./bin/www.js",
    

    配置执行环境(这个是必须写的哇)

    #! /usr/bin/env node
    
    // 这个就是我们的入口文件啦~~
    
    console.log('xiaojin love code!')
    

    配置环境映射?怎么说,这个就是把这个环境临时搞到全局,让cmd也可以用

    npm link
    

    当然,还有更好玩的东西

    • 我把命令改了一下,好玩多了
    {
      "name": "vite",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "bin":{
        "xiaojin": "./bin/www.js"
      },
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "dependencies": {
        "koa": "^2.13.4"
      }
    }
    
    

    新建一个文件编写服务代码,先跑一把玩一下 vite\src\server.js

    const Koa = require('koa')
    const { Static } = require('vue')
    function createServer() {
        let app = new Koa()
        // 实现静态服务功能,访问我们的服务器可以返回对应的文件koa-Static
        return app
    }
    createServer().listen(8088,()=>{
        console.log('xiaojin server is start at 8088')
    })
    
    
    

    使用nodemon来监控代码变化,然后来自动重新执行,我们要执行下面的命令,其实我之前用过另一个叫做 surpervisor,也是可以的

    npm i nodemon
    

    在外层新建一个配置文件,监控这个目录的代码变化 nodemon.json

    {
        "watch":["vite"],
        "exec":"xiaojin"
    }
    


    记得配置这个

    重新执行nodemon命令试一把,记得,是在外层哦

    nodemon
    

    下个步骤,等我今天晚上熬夜写完继续补充

    来啦~~

    接下来我们来实现,通过服务访问静态资源

    • 就直接用我们外层的那个文件 index.html,添加几句测试代码
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <link rel="icon" href="/favicon.ico" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Vite App</title>
      </head>
      <body>
        测试代码哦~
        测试一下我们的静态资源读取情况~
        <div id="app"></div>
        <script type="module" src="/src/main.js"></script>
      </body>
    </html>
    
    
    • 安装 koa-static
    PS C:\jin_files\code\testDemo\vite-demo\my-vue-app> cd vite
    PS C:\jin_files\code\testDemo\vite-demo\my-vue-app\vite> npm install koa-static
    

    • 新增vite\src\serverPluginServerStatic.js,编写静态插件调用的逻辑代码
    const static = require('koa-static')
    
    function staticPlugin({app,root}) {
      app.use(static(root)) // 这个root指的是根目录哦~~当我访问我的服务localhost:8088的时候,它会去找根目录下面的index.html
    }
    module.exports = staticPlugin
    
    • 在server.js里,编辑代码,搞一下插件,先来实现静态服务功能
    const Koa = require('koa')
    const staticPlugin = require('./serverPluginServerStatic')
    function createServer() {
        let app = new Koa()
        // 实现静态服务功能,访问我们的服务器可以返回对应的文件koa-Static
        const context = { // 创建一个上下文,给不同插件共享功能
            app,
            root:process.cwd() // 这个目录就在vite-vue
        }
        const resolvePlugin = [staticPlugin]
        resolvePlugin.forEach(plugin => plugin(context))
        return app
    }
    createServer().listen(8088,()=>{
        console.log('xiaojin server is start at 8088')
        console.log('修改代码跑一把')
    })
    
    
    
    • 关键代码标记给大家:

    • 然后,去浏览器里输入http://localhost:8088/,看一下效果吧~~

    • 很成功哇,我们的服务调用了静态资源,运行起来啦

    配置一下,访问public目录试一把

    • 根路径下,创建测试文件public\test.txt
    I am xiaojin ,I love code~
    

    • 进入vite\src\serverPluginServerStatic.js添加代码
    const static = require('koa-static')
    const path = require('path')
    function staticPlugin({app,root}) {
      app.use(static(root)) // 这个root指的是根目录哦~~当我访问我的服务localhost:8088的时候,它会去找根目录下面的index.html
      app.use(static(path.resolve(root,'public')))
    }
    module.exports = staticPlugin
    

    • 访问http://localhost:8088/test.txt,试一把~
    • 成功啦~~

    打开F12看一下

    • 啊哦~ 报错了
    • Uncaught TypeError: Failed to resolve module specifier "vue". Relative references must start with either "/", "./", or "../".

    怎么才能正常显示我们的资源不报错呢???就是说,如何让这句话 import { createApp } from 'vue' 正常执行?

    冷静分析

    • es6模块会自动发送请求,查找相应文件
    • vite所做的事情就是: 根据你需要的文件,进行改写
    • 我们发请求,请求了main.js文件
    • 我们可以在后端将main.js里的内容进行改写操作,将所有不带./ ../ / 的全部加一个/@modules去寻找相应资源,不就不会报错了吗??

    添加模块重写代码逻辑,重写我们的请求路径哇~~

    • 新建文件 vite\src\serverPluginModuleRewrite.js
    
    function reWritePlugin({app,root}) {
    app.use(async (ctx, next) =>{
        // todu:
        await next() // 先走静态服务,因为默认会先执行 静态服务中间件,将结果放到ctx.body
        // 需要将流转换为字符串,而且我们只需要处理其中JS的引用问题
        console.log('reWritePlugin:')
        if(ctx.body && ctx.response.is('js')){
            console.log(ctx.body)
            console.log('ctx.body已打印完毕')
        }
    
    })
    }
    module.exports = reWritePlugin
    
    • 添加插件引用代码到vite\src\server.js
    const reWritePlugin = require('./serverPluginModuleRewrite')
    function createServer() {
        ...
        const resolvePlugin = [
            reWritePlugin, // 重写请求路径插件 ,为什么这么写这个顺序呢,原因解释放到插件里吧~~
            staticPlugin, // 静态服务插件
        ]
        ...
    }
    

    • 访问http://localhost:8088/,然后查看你的命令行

    好了,我要睡觉了,明天继续写

    睡觉中......

    继续开工啦~~

    接下来开始读取文件流,

    • 解决 vite\src\serverPluginModuleRewrite.js 中需要将流转换为字符串,而且我们只需要处理其中JS的引用问题
    • 创建vite\src\utils.js
    const {Readable} = require('stream')
    async function readBody(stream) {
        if(stream instanceof Readable) {
            return new Promise((resolve,reject)=>{
                let res = ''
                stream.on('data',function(chunk){
                    res += chunk
                })
                stream.on('end',function() {
                    resolve(res)
                })
            })
        }else{
            return stream
        }
    }
    exports.readBody = readBody
    
    • 修改代码vite\src\serverPluginModuleRewrite.js
    const { readBody } = require("./utils")
    
    function reWritePlugin({app,root}) {
    app.use(async (ctx, next) =>{
        // todu:
        await next() // 先走静态服务,因为默认会先执行 静态服务中间件,将结果放到ctx.body
        // 需要将流转换为字符串,而且我们只需要处理其中JS的引用问题
        console.log('reWritePlugin:')
        if(ctx.body && ctx.response.is('js')){
            // console.log(ctx.body) // 这个打出来是main.js的文件流
            let res = await readBody(ctx.body)
            console.log(res)
            console.log('ctx.body已打印完毕')
        }
    
    })
    }
    module.exports = reWritePlugin
    


    访问http://localhost:8088/,试一把

    打印完成啦~~开心(),接下来我们开始解析并改写文件内容(就是把VUE文件解析处理为 @/modules)

    • 我们需要安装插件,处理字符串
    • es-module-lexer 可以解析所有的import语法
    • magic-string 处理字符串,把字符串变成对象类型,引用类型
    • 外层执行:
     
    npm install es-module-lexer magic-string
    
    

    • 给 vite\src\serverPluginModuleRewrite.js 添加解析代码
    const {parse} = require('es-module-lexer')
    function reWriteImports(source) {
        let imports = parse(source)
        console.log(imports)
    }
    
    

    • 刷新一下页面,看打印结果
    修改代码跑一把
    reWritePlugin:
    reWritePlugin:
    [
      [
        { n: 'vue', s: 27, e: 30, ss: 0, se: 31, d: -1, a: -1 },
        { n: './App.vue', s: 49, e: 58, ss: 32, se: 59, d: -1, a: -1 }
      ],
      [],
      false
    ]
    reWriteRes已打印完毕
    
    
    • 解释一下这个是什么哦

    开始编写重写文件逻辑啦

    const { readBody } = require("./utils")
    const { parse } = require('es-module-lexer')
    const MagicString = require('magic-string')
    function reWriteImports(source) {
        let imports = parse(source)[0]
        // console.log(imports)
        // 路径处理逻辑开始咯
        let ms = new MagicString(source)
        if (imports.length > 0) {
            imports.forEach(_ => {
                let { s, e, n } = _
                let id = n
                console.log('old id :', id)
                if (/^[^\/\.]/.test(id)) {
                    id = `/@modules/${id}`
                    console.log('new id :', id)
                    ms.overwrite(s, e, id)
                }
            })
        }
        return ms.toString()
    }
    
    function reWritePlugin({ app, root }) {
        app.use(async (ctx, next) => {
            // todu:
            await next() // 先走静态服务,因为默认会先执行 静态服务中间件,将结果放到ctx.body
            // 需要将流转换为字符串,而且我们只需要处理其中JS的引用问题
            console.log('reWritePlugin:')
            if (ctx.body && ctx.response.is('js')) {
                // console.log(ctx.body) // 这个打出来是main.js的文件流
                let res = await readBody(ctx.body)
                const reWriteRes = reWriteImports(res)
                ctx.body = reWriteRes
                // console.log(reWriteRes)
                console.log('reWriteRes已打印完毕')
            }
    
        })
    }
    module.exports = reWritePlugin
    
    • 重点改了这里哦

    • 给大家看一下打印出来的内容

    old id : vue
    new id : /@modules/vue
    old id : ./App.vue
    

    跑一把试一下,main.js已经被改写成功了呢~~~~

    继续睡觉啦,明天接着写~~

  • 相关阅读:
    JS生成Guid
    MVC——分页
    MVC入门——删除页
    MVC入门——编辑页
    MVC入门——详细页
    MVC入门——列表页
    MVC入门——增
    pandas使用
    简单线性回归预测实现
    flask 自定义url转换器
  • 原文地址:https://www.cnblogs.com/sugartang/p/16361768.html
Copyright © 2020-2023  润新知