阅读 514

Vite的介绍

Vite

  • Vite是一个面向现代浏览器的一个更轻、更快的web开发应用

  • 基于ECMAScript标准原生模块系统(ESM)实现

Vite项目依赖项

  • Vite

  • @vue/compiler-sfc

Vite的特点

  • 快速冷启动

  • 模块热更新

  • 按需编译

  • 开箱即用

vite serve

1608286660.jpg

vue-cli-service serve

1608286806.jpg

vite只有当文件请求时才会去编译相应的模块

  • vite只有在请求时才会去编译文件,文件修改也只会编译当前文件

  • webpack HMR会以这个文件为入口重新build一次,所有涉及的依赖都会被重新加载一遍

vite build

  • Rollup 打包

  • 动态导入

使用webpack的原因

  • 浏览器环境不支持模块化

  • 零散的模块文件会产生大量的http请求(http2可以解决通过复用链接)

开箱即用

  • Typescript内置支持

  • less/sass/stylus/postcss内置支持 需要单独安装依赖

  • jsx

  • Web Assembly

Vite的核心功能

vite在启动时,会把当前项目作为静态服务器的根目录,并拦截服务器的部分文件请求,当遇到浏览器不能识别的模块时会进行编译,通过websocked实现hmr

Vite的实现原理

静态web服务器

npm init -y 复制代码

在package.json文件中设置name为vite-cli即脚手架的启动命令为vite-cli

首先启动一个web服务器

#!/usr/bin/env node const Koa = require('koa') const send = require('koa-send') const app = new Koa() app.use(async (ctx,next) => {     await send(ctx, ctx.path, {         root: process.cwd(),         index: 'index.html'     })     await next() }) app.listen(3000, () => {     console.log('serve listen 3000') }) 复制代码

使用npm link或yarn link 将这个脚手架工具安装到本地全局

npm link # 启动 vite-cli 复制代码

处理javascript请求

// stream转换成字符串 function streamToString(text) {     return new Promise((resolve, reject) => {         let chunks = []         text.on('data', chunk => chunks.push(chunk))         text.on('error', reject)         text.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')))     }) } app.use(async (ctx, next) => {     if (ctx.type === 'application/javascript') {         //   ctx.body 是stream类型,需转换成字符串         ctx.body = await streamToString(ctx.body)     }     await next() }) 复制代码

加载第三方模块

首先需要将上面JavaScript代码中import的第三方包路径修改为'/@modules/',然后对第三方模块路径进行处理

// 处理第三方包路径  应该放在最前面 app.use(async (ctx, next) => {     if (ctx.path.startsWith('/@modules/')) {         let moduleName = ctx.path.substr(10)         let modulePath = path.resolve(process.cwd(), 'node_modules', moduleName)         let pkg = require(path.resolve(modulePath, 'package.json'))         ctx.path = path.join('/node_modules', moduleName, pkg.module)     }     await next() }) ... app.use(async (ctx, next) => {     if (ctx.type === 'application/javascript') {         // ctx.body 是stream类型         let text = await streamToString(ctx.body)         // 修改第三方模块包的路径         ctx.body = text.replace(/(from\s+['"])(?!.\/)/g, '$1/@modules/')     }     await next() }) 复制代码

编译单文件组件

首先需要修改vue文件格式为以下格式

import {render as __render} from "/aa.vue?type=template"  __script.render = __render export default __script 复制代码

第二次请求vue文件时,输出编译后的代码

// 编译文件 const build = async (ctx, next) => {     // 编译单文件组件     if (ctx.path.endsWith('.vue')) {         const contents = await streamToString(ctx.body)         const {             descriptor         } = compilerSfc.parse(contents)         let code         if (!ctx.query.type) {             code = descriptor.script.content             code = code.replace(/export\s+default\s+/g, 'const __script = ')             code += `             import {render as __render} from "${ctx.path}?type=template"             __script.render = __render             export default __script             `         } else if (ctx.query.type === 'template') {             let template = compilerSfc.compileTemplate({                 source: descriptor.template.content,                 filename: descriptor.filename,                 id: ctx.path             })             code = template.code         }         ctx.body = stringToStream(code)         ctx.type = 'application/javascript'     }     await next() } 复制代码

处理外部引入的css文件 首先需要将js代码中引入的css文件路径替换为xxx@import

app.use(async (ctx, next) => {     if (ctx.type === 'application/javascript') {         // ctx.body 是stream类型         let text = await streamToString(ctx.body)         // 修改第三方模块包的路径         ctx.body = text.replace(/(from\s+['"])(?![.\/])/g, '$1/@modules/')             .replace(/process\.env\.NODE_ENV/g, '"develop"')             // css             .replace(/(import\s+['"][\w\.\/]+\.css)(['"])/g,'$1?import$2')     }     await next() }) 复制代码

然后把css代码添加到html样式中

... if (ctx.path.endsWith('.css')) {     const css = await streamToString(ctx.body)     let code = `         import {updateStyle} from '/build/util/style.js'         const css = ${JSON.stringify(css)}         updateStyle(css)         export default css     `     ctx.body = stringToStream(code)     ctx.type = 'application/javascript' } ... export function updateStyle(content,id){     const style = document.createElement('style')     style.setAttribute('type', 'text/css')     style.innerHTML = content     document.head.appendChild(style) } 复制代码

完整代码

入口文件

#!/usr/bin/env node const Koa = require('koa') const send = require('koa-send') const path = require('path') const {     static,     npmHandler,     build } = require('./middlewares/index.js') const {     streamToString } = require('./util') const app = new Koa() // 处理第三方依赖 app.use(npmHandler) // 处理静态资源 app.use(static) app.use(build) // 修改第三方模块依赖 app.use(async (ctx, next) => {     if (ctx.type === 'application/javascript') {         // ctx.body 是stream类型         let text = await streamToString(ctx.body)         // 修改第三方模块包的路径         ctx.body = text.replace(/(from\s+['"])(?![.\/])/g, '$1/@modules/')             .replace(/process\.env\.NODE_ENV/g, '"develop"')             .replace(/(import\s+['"][\w\.\/]+\.css)(['"])/g,'$1?import$2')     }     await next() }) app.listen(3000) console.log('server listen http://localhost:3000') 复制代码

middlewares

const send = require('koa-send') const path = require('path') const compilerSfc = require('@vue/compiler-sfc') const {     streamToString,     stringToStream } = require('../util') // 静态资源 const static = async (ctx, next) => {     await send(ctx, ctx.path, {         root: process.cwd(),         index: 'index.html'     })     await next() } // 处理第三方依赖包 const npmHandler = async (ctx, next) => {     if (ctx.path.startsWith('/@modules/')) {         let moduleName = ctx.path.substr(10)         let modulePath = path.resolve(process.cwd(), 'node_modules', moduleName)         let pkg = require(path.resolve(modulePath, 'package.json'))         ctx.path = path.join('/node_modules', moduleName, pkg.module)     }     await next() } // 编译文件 const build = async (ctx, next) => {     // 编译单文件组件     if (ctx.path.endsWith('.vue')) {         const contents = await streamToString(ctx.body)         const {             descriptor         } = compilerSfc.parse(contents)         let code         if (!ctx.query.type) {             code = descriptor.script.content             code = code.replace(/export\s+default\s+/g, 'const __script = ')             code += `             import {render as __render} from "${ctx.path}?type=template"             __script.render = __render             export default __script             `         } else if (ctx.query.type === 'template') {             let template = compilerSfc.compileTemplate({                 source: descriptor.template.content,                 filename: descriptor.filename,                 id: ctx.path             })             code = template.code             console.log(descriptor.filename)         }           ctx.body = stringToStream(code)         ctx.type = 'application/javascript'     } else if (ctx.path.endsWith('.css')) {         const css = await streamToString(ctx.body)         let code = `          import {updateStyle} from '/build/util/style.js'            const css = ${JSON.stringify(css)}            updateStyle(css)             export default css         `         ctx.body = stringToStream(code)         ctx.type = 'application/javascript'     }     await next() } module.exports = {     static,     npmHandler,     build } 复制代码

utils

const {     Readable } = require('stream') // stream 转换成 string function streamToString(text) {     return new Promise((resolve, reject) => {         let chunks = []         text.on('data', chunk => chunks.push(chunk))         text.on('error', reject)         text.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')))     }) } function stringToStream(text) {     const stream = new Readable()     stream.push(text)     stream.push(null)     return stream } module.exports = {     streamToString,     stringToStream } 复制代码

style

export function updateStyle(content,id){     const style = document.createElement('style')     style.setAttribute('type', 'text/css')     style.innerHTML = content     document.head.appendChild(style) }


作者:shibin
链接:https://juejin.cn/post/7021772569552355335


文章分类
后端
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐