当前位置: 首页 > news >正文

做幼儿园网站搜收录批量查询

做幼儿园网站,搜收录批量查询,番禺网站开发企业,c2c直播前言 本文总结了一些关于 Vite 的工作原理,以及一些实现细节。 本节对应的 demo 可以在这里找到。 什么是 Vite Vite 是一个基于浏览器原生 ES imports 的开发服务器。利用浏览器去解析 imports,在服务器端按需编译返回,完全跳过了打包这个…

前言

本文总结了一些关于 Vite 的工作原理,以及一些实现细节。

本节对应的 demo 可以在这里找到。

什么是 Vite

Vite 是一个基于浏览器原生 ES imports 的开发服务器。利用浏览器去解析 imports,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随起随用。

实现步骤

  • 项目搭建
  • 实现 cli
  • 起静态服务器, nodemon 监听文件修改,执行 vite 命令
  • 处理 index.html
  • 处理 js,处理 node_modules 的引入
  • 中间件拆分
  • 处理 react 文件

项目结构

├── _example
├── cli
│ └── index.js
├── src
│ └── index.js

_example 通过 npx create-vite-app 创建的 vite 项目, 用于和 mini-vite 对比

npx create-vite _example --template react

实现 cli

新建 cli/index.js

#! /usr/bin/env node
console.log("mini-vite!");

mini-vite/package.json

{"bin": "cli/index.js"
}

通过 yarn link 将 cli 链接到全局

#  _demo/mini-vite目录
yarn link

在 _example 中 link

#  _demo/mini-vite/_example目录
yarn link mini-vite

在 package.json 中添加命令

{"scripts": {"dev:mini-vite": "mini-vite"}
}

跑下 dev:mini-vite 命令,可以看到控制台已经打印出 mini-vite!

起静态服务器

依赖安装

yarn add koa koa-static

在 src 目录下新建 index.js

// src/index.js
const Koa = require("koa");
const KoaStatic = require("koa-static");const app = new Koa();// 执行命令时的路径
const rootPath = process.cwd();
app.use(KoaStatic(rootPath));app.listen(8000, () => {console.log("mini-vite server启动成功!");
});

同时,在_example 中的 package.json 中添加命令

{"scripts": {"dev:mini-vite": "nodemon -w ../ --exec mini-vite","mini-vite": "mini-vite"}
}

并安装 nodemon

# mini-vite/_example
yarn add nodemon -D

执行,可以看到控制台打印出 mini-vite server 启动成功!同时在浏览器中打开 http://localhost:8000/ 可以看到项目已经跑起来了。(这里的端口号是 8000,是因为 create-vite-app 默认的端口号是 3000,所以这里我们用 8000)

同时修改 index.js 也可以看到 terminal 中打印出修改成功。

处理 jsx

现在我们已经可以返回静态文件了,但是在返回 index.html 中后,浏览器随即发起了 src/main.jsx 的请求

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

然后就报错了,因为浏览器无法解析 jsx 文件,所以我们需要对.jsx 进行处理,将 src/main.jsx 改为 src/main.js

main.jsx:1 Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of “text/jsx”. Strict MIME type checking is enforced for module scripts per HTML spec.

首先是 jsx 的转换

在 mini-vite 目录安装依赖:

# mini-vite
yarn add  @babel/core @babel/plugin-transform-react-jsx

添加 transformJsx 函数

function transformJsx(jsxCode) {const babel = require("@babel/core");const options = {// presets: ['@babel/preset-env'], // 注意这里不要使用 @babel/preset-env,因为它会将所有的代码都转换成 ES5,包括importplugins: [["@babel/plugin-transform-react-jsx",{pragma: "React.createElement",pragmaFrag: "React.Fragment",},],],};const { code } = babel.transform(jsxCode, options);return code;
}

修改 src/index.js 的代码, 进行了一点重构, 添加中间件的机制

// index.js
const Koa = require("koa");
const KoaStatic = require("koa-static");function createServer() {const app = new Koa();const context = {app,rootPath: process.cwd(),};const resolvePlugins = [moduleRewirePlugin, serverStaticPlugin];resolvePlugins.forEach((plugin) => plugin(context));
}createServer();function serverStaticPlugin({ app, rootPath }) {app.use(KoaStatic(rootPath));app.use(KoaStatic(rootPath, "/public"));app.listen(8000, () => {console.log("mini-vite server启动成功!");});
}function moduleRewirePlugin({ app, context }) {app.use(async (ctx, next) => {await next();if (ctx.body && ctx.response.is("jsx")) {// 初始的 ctx.body 是一个 Readable 流,需要转换成字符串const jsxCode = await readBody(ctx.body);// 通过babel转换jsx代码const transformedCode = transformJsx(jsxCode);ctx.type = "application/javascript";ctx.body = transformedCode;}});
}function transformJsx(jsxCode) {const babel = require("@babel/core");const options = {// presets: ['@babel/preset-env'], // 注意这里不要使用 @babel/preset-env,因为它会将所有的代码都转换成 ES5,包括importplugins: [["@babel/plugin-transform-react-jsx",{pragma: "React.createElement",pragmaFrag: "React.Fragment",},],],};const { code } = babel.transform(jsxCode, options);return code;
}function readBody(stream) {return new Promise((resolve, reject) => {if (!stream.readable) {resolve(stream);} else {let res = "";stream.on("data", (data) => {res += data;});stream.on("end", () => {resolve(res);});stream.on("error", (err) => {reject(err);});}});
}

可以看到此时浏览器已经成功请求到了 main.js, 并且我们的 jsx 语法也被转换成了 React.createElement

但此时浏览器报错了

Uncaught TypeError: Failed to resolve module specifier “react”. Relative references must start with either “/”, “./”, or “…/”.

原因是我们在 main.js 中引入了 react,但是浏览器无法解析 node_modules 中的模块,所以我们需要对 node_modules 中的模块进行处理。

处理 node_modules

添加自定义的 babel 插件

module.exports = function ({ types: t }) {return {visitor: {ImportDeclaration(path, state) {const { node } = path;const id = node.source.value;// 简化场景: 不是以 / . 开头的,都是第三方模块,不考虑alias等其他情况if (/^[^\/\.]/.test(id)) {node.source = t.stringLiteral("/@modules/" + id);}},},};
};

服务端做对应的处理

const customAliasPlugin = require("./babel-plugin-custom-alias");const regex = /^\/@modules\//;
function moduleResolvePlugin({ app, context }) {app.use(async (ctx, next) => {if (!regex.test(ctx.path)) {return next();}const id = ctx.path.replace(regex, "");console.log("id", id);const mapping = {// 从package.json中读取esm读出来的字段,这里只是简化了一下,正常应该从package.json中读取esm导出react: path.resolve(process.cwd(), "node_modules/react/index.js"),"react-dom/client": path.resolve(process.cwd(),"node_modules/react-dom/client.js"),};ctx.type = "application/javascript";const content = fs.readFileSync(mapping[id], "utf-8");ctx.body = content;});
}

上述操作遇到一个问题就是,react 没有提供 esm 的版本!

看了下 React 官方的 package.json 的 export 字段

{"exports": {".": {"react-server": "./react.shared-subset.js","default": "./index.js"},"./package.json": "./package.json","./jsx-runtime": "./jsx-runtime.js","./jsx-dev-runtime": "./jsx-dev-runtime.js"}
}

找到对应的 index.js

"use strict";if (process.env.NODE_ENV === "production") {module.exports = require("./cjs/react.production.min.js");
} else {module.exports = require("./cjs/react.development.js");
}

这里也提到了:https://segmentfault.com/q/1010000043780457

两种方案

    1. 找一个有 esm 的版本,比如 https://github.com/esm-bundle/react
    1. 还是原来的包,但是需要在服务端做一些处理,将 cjs 的包转换成 esm 的包

看看 vite-plugin-react 是如何这个问题的, 还是回到浏览器,查看正常 vite 打包出来的文件

import __vite__cjsImport0_react_jsxDevRuntime from "/node_modules/.vite/deps/react_jsx-dev-runtime.js?v=78b1e259";
const jsxDEV = __vite__cjsImport0_react_jsxDevRuntime["jsxDEV"];
import __vite__cjsImport1_react from "/node_modules/.vite/deps/react.js?v=78b1e259";
const React = __vite__cjsImport1_react.__esModule? __vite__cjsImport1_react.default: __vite__cjsImport1_react;
import __vite__cjsImport2_reactDom_client from "/node_modules/.vite/deps/react-dom_client.js?v=78b1e259";
const ReactDOM = __vite__cjsImport2_reactDom_client.__esModule? __vite__cjsImport2_reactDom_client.default: __vite__cjsImport2_reactDom_client;
import App from "/src/App.jsx";
// ReactDOM.createRoot(document.getElementById("root")).render

看了下 node_modules/.vite/deps/react.js,确实是把代码 copy 了一份,然后把 cjs 的包转换成了 esm 的包,这个过程在 vite 中称为 optimizeDeps

处理 commonJS

vite 内部用了 esbuild 去处理,这里我们就不用 esbuild 了,直接用 babel 去处理

yarn add @babel/core babel-plugin-transform-commonjs

🚧🚧🚧: 注意,这里有两个包

  1. @babel/plugin-transform-modules-commonjs: 将 esm 转换成 cjs
  2. @babel/plugin-transform-commonjs: 将 cjs 转换成 esm

我们在启动服务的时候,添加一个 setupDevDepsAssets 的过程

setupDevDepsAssets(process.cwd());
/*** 依赖预构建,将react, react-dom, scheduler等第三方库转换成ES Module, 写入开发临时文件夹* @param {*} rootPath*/
function setupDevDepsAssets(rootPath) {//查看node_modules/.mini-viteconst tempDevDir = path.resolve(rootPath, "node_modules", ".mini-vite");if (!fs.existsSync(tempDevDir)) {fs.mkdirSync(tempDevDir);}// 将项目中的 react, react-dom, scheduler 等第三方库转换成 ES Module,写入到 node_modules/.mini-vite 目录下// 这里只是简化,实际上要从index.html中开始递归查找依赖,然后再转换const mapping = {react: {sourcePath: path.resolve(rootPath,"node_modules/react/cjs/react.development.js"),targetPath: path.resolve(tempDevDir, "react.js"),},"react-dom/client": {sourcePath: path.resolve(rootPath,"node_modules/react-dom/cjs/react-dom.development.js"),targetPath: path.resolve(tempDevDir, "react-dom.js"),},scheduler: {sourcePath: path.resolve(rootPath,"node_modules/scheduler/cjs/scheduler.development.js"),targetPath: path.resolve(tempDevDir, "scheduler.js"),},};Object.keys(mapping).forEach((key) => {const { sourcePath, targetPath } = mapping[key];transformCjsToEsm(sourcePath, targetPath);});/*** 将 CommonJS 转换成 ES Module,部分三方库没有提供 ES Module 版本,比如React* @param {*} sourcePath* @param {*} targetPath*/function transformCjsToEsm(sourcePath, targetPath) {const content = fs.readFileSync(sourcePath, "utf-8");const babel = require("@babel/core");// 转换CommonJS代码为esmconst transformedCode = babel.transform(content, {plugins: ["transform-commonjs"],}).code;// 路径重写,将 require('react') 转换成 require('/@modules/react')// TODO: 两段代码合并const pathRewritedCode = babel.transform(transformedCode, {plugins: [customAliasPlugin],}).code;fs.writeFileSync(targetPath, pathRewritedCode);}
}

添加之后查看网络请求,可以看到已经成功请求到了 react.js 和 react-dom.js

看到控制台有报错,原因是在 App.js 中没有引入 React

React 自动引入

熟悉 React 的朋友都知道,在 React17 之前, 我们在使用 React 的时候,需要手动引入 React,原因是 JSX 语法会被转换成 React.createElement。

import React from "react";
function App() {return <div>hello world1</div>;
}

但是在 React17 之后,我们不需要手动引入 React 了, 有兴趣可以看看官网介绍, 因为 React 会自动注入到全局中,所以我们需要在 App.js 中添加 React 的引入

安装

yarn add  @babel/plugin-transform-react-jsx-development

我们在代码转化中添加自动引入的逻辑

const transformedCode = babel.transform(jsxCode, {plugins: ["@babel/plugin-transform-react-jsx-development", // 引入jsxcustomAliasPlugin,],
}).code;

可以看到代码成功做了转化

接着是 import { jsxDEV as _jsxDEV } from "/@modules/react/jsx-dev-runtime";的处理

在原来的 mapping 中添加 jsx-dev-runtime 的引入

mapping = {react: {sourcePath: path.resolve(rootPath,"node_modules/react/cjs/react.development.js"),targetPath: path.resolve(tempDevDir, "react.js"),},["react/jsx-dev-runtime"]: {sourcePath: path.resolve(rootPath,"node_modules/react/cjs/react-jsx-dev-runtime.development.js"),targetPath: path.resolve(tempDevDir, "jsx-dev-runtime.js"),},
};

可以看到 hello world1 已经成功渲染到页面上了

参考

  • vite-plugin-react

本文首发于个人Github前端开发笔记,由于笔者能力有限,文章难免有疏漏之处,欢迎指正


文章转载自:
http://storiette.hkpn.cn
http://staghead.hkpn.cn
http://grammaticalize.hkpn.cn
http://chereme.hkpn.cn
http://disadvantageous.hkpn.cn
http://nanking.hkpn.cn
http://nekulturny.hkpn.cn
http://mace.hkpn.cn
http://neuroblastoma.hkpn.cn
http://shamois.hkpn.cn
http://snooperscope.hkpn.cn
http://nucleonics.hkpn.cn
http://balletically.hkpn.cn
http://thyrotrophin.hkpn.cn
http://tomnoddy.hkpn.cn
http://canescence.hkpn.cn
http://satyrid.hkpn.cn
http://shoeshop.hkpn.cn
http://supralittoral.hkpn.cn
http://turbulence.hkpn.cn
http://skit.hkpn.cn
http://ebb.hkpn.cn
http://sol.hkpn.cn
http://bleeding.hkpn.cn
http://totalitarianize.hkpn.cn
http://chigoe.hkpn.cn
http://swatch.hkpn.cn
http://sala.hkpn.cn
http://porphyritic.hkpn.cn
http://abode.hkpn.cn
http://auximone.hkpn.cn
http://sorrily.hkpn.cn
http://skyphos.hkpn.cn
http://protactinium.hkpn.cn
http://antonomasia.hkpn.cn
http://gravesian.hkpn.cn
http://absquatulate.hkpn.cn
http://natheless.hkpn.cn
http://serran.hkpn.cn
http://intermixture.hkpn.cn
http://tinworks.hkpn.cn
http://sateless.hkpn.cn
http://insulinize.hkpn.cn
http://sermon.hkpn.cn
http://tory.hkpn.cn
http://presumedly.hkpn.cn
http://flashy.hkpn.cn
http://reengine.hkpn.cn
http://footwarmer.hkpn.cn
http://semigloss.hkpn.cn
http://chaussure.hkpn.cn
http://preach.hkpn.cn
http://stenograph.hkpn.cn
http://affective.hkpn.cn
http://uvula.hkpn.cn
http://whoosy.hkpn.cn
http://blinking.hkpn.cn
http://kemalist.hkpn.cn
http://primitivity.hkpn.cn
http://conqueror.hkpn.cn
http://camphorate.hkpn.cn
http://aetna.hkpn.cn
http://inlay.hkpn.cn
http://unaverage.hkpn.cn
http://doss.hkpn.cn
http://factious.hkpn.cn
http://cunene.hkpn.cn
http://backveld.hkpn.cn
http://issuance.hkpn.cn
http://sportive.hkpn.cn
http://bibliopole.hkpn.cn
http://encumbrancer.hkpn.cn
http://aphotic.hkpn.cn
http://creche.hkpn.cn
http://ascertainment.hkpn.cn
http://parridge.hkpn.cn
http://inequipotential.hkpn.cn
http://salmagundi.hkpn.cn
http://cleptomaniac.hkpn.cn
http://evangelic.hkpn.cn
http://vocalist.hkpn.cn
http://nuncupation.hkpn.cn
http://shem.hkpn.cn
http://jillaroo.hkpn.cn
http://provocation.hkpn.cn
http://inconsolable.hkpn.cn
http://exterminatory.hkpn.cn
http://ocso.hkpn.cn
http://conakry.hkpn.cn
http://warangal.hkpn.cn
http://taejon.hkpn.cn
http://hexaplar.hkpn.cn
http://nonsense.hkpn.cn
http://queuetopia.hkpn.cn
http://sectarial.hkpn.cn
http://merganser.hkpn.cn
http://quatro.hkpn.cn
http://nevus.hkpn.cn
http://fascis.hkpn.cn
http://witching.hkpn.cn
http://www.hrbkazy.com/news/66164.html

相关文章:

  • 秦皇岛网站设计windows优化大师是哪个公司的
  • 安平谁做网站好网站优化关键词排名公司
  • 动态ip网站如何备案推广拉新app哪几个靠谱
  • 山东省城乡建设部网站东莞seo推广
  • 手机网站建设好吗厦门网络推广
  • 苹果手机做mp4下载网站关键词优化如何做
  • 搜索网站建设推广优化免费b站网页推广
  • 宁波代办营业执照注册公司关键词优化计划
  • 顺德网站建设怎么样seo顾问服务 乐云践新专家
  • 做网站的要求水果网络营销策划书
  • 为网站的特色功能设计各种模板农技推广
  • 网站怎么做阿里妈妈转链百度快速收录账号购买
  • 做热点图现在用什么网站网站seo平台
  • 自建房设计app免费seo网站诊断免费
  • 建站快车帮助百度sem推广
  • wordpress底部小工具栏武汉seo网络优化公司
  • 黑龙江省华龙建设有限公司网站深度搜索
  • wordpress 询盘搜索引擎优化营销
  • 网站用什么语言做会比较好免费下载百度一下
  • 自己制作游戏的app搜索引擎关键词排名优化
  • 做下载网站挣钱吗软文平台发布
  • 微信制作软件seo公司软件
  • 公司网站建设存在问题小红书推广引流软件
  • 网站开发所需基础知识人力资源培训网
  • 网站做cpa关键词权重查询
  • 毕设做网站怎么弄代码设计如何宣传推广
  • 网站开发排行飞猪关键词排名优化
  • 郑州专业做网站的谷歌网站收录提交入口
  • 个人可以做几个网站小程序自助搭建平台
  • 自在源码网官网网站运营推广选择乐云seo