首页 > 其他分享 >vue-cli原理

vue-cli原理

时间:2024-11-27 19:55:51浏览次数:12  
标签:vue cli res app 中间件 js webpack fs 原理

webpack

1. package.json命令启动配置环境变量

cross-env是一个用于跨平台设置和使用环境变量的脚本工具, cross-env NODE_ENV=online就是设定NODE_ENV的值是"online".
在webpack.config.js里可以通过process.env.NODE_ENV获取

{
    "dev": "cross-env NODE_ENV=online node build/dev-server.js",
}

2. node-sass安装问题处理

node-sass由于和node版本关联太深, 很容易下载不下来, 不建议继续使用而是使用sass.

  1. package.json里去掉node-sass
  2. "/deep/" 全局替换成 "::v-deep"

3. webpack的配置文件的本地调试.

  1. 在配置webpack.config.js时, 我们需要测试配置文件是否正确, 看导入库是否正常, 变量值是否正确, 就需要进行断点调试.
    原来一直是输出log, 但是那样不是很直观, 不好看代码是怎么走流程的. 而断点调试可以.
  2. 点击运行脚本右边的设置按钮,出现 launch.json. 这是启动配置
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "启动程序",
            "skipFiles": [
                "<node_internals>/**"
            ],
            "program": "${workspaceFolder}\\vue2-elm-master\\build\\build.js"
        },
        {
            "type": "node-terminal", // 环境 node
            "name": "运行脚本: vue2-elm-build", // 名称
            "request": "launch",
            "command": "npm run build", // 运行的终端命令
            "cwd": "${workspaceFolder}\\vue2-elm-master" , //根目录地址.
        }
    ]
}

调试方法

4. 本地开发开启服务器的原因

  1. 解决线上部署后的资源路径问题: https://www.cnblogs.com/zhuzhenwei918/p/6866094.html
  2. 解决history模式下的URL fallback问题: https://router.vuejs.org/zh/guide/essentials/history-mode.html

5. express的中间件.

通过浏览器, 地址: localhost:3000 可以看到返回值.

  1. app.get("/", ()=>{}), 会处理 localhost:3000/ 的请求
  2. app.use是中间件, 可以在app.get前/后进行响应处理. 比如修改地址, 添加记录,等.
  3. app.use(()=>{}), 的第一个参数是函数, 那就是全局中间件, 监控所有的请求.
  4. app.use(path, ()=>{}), 的第一个参数是字符串路径, 那就是路由中间件, 监控特定的请求.
  5. app.use((err, req, res, next)=>{}), 的函数参数, 如果有四个参数, 那么第一个参数就会变成error,可以监控报错信息.

关于异常中间件

  1. 异常中间件全局只会生效一个, 就是第一个, 经过了第一个异常中间件后, 报错就被处理了, 第二个异常中间件就接受不到了.
  2. 异常中间件可以传递给普通中间件. 异常中间件的next()执行后, 回到下一个普通的中间件.
  3. 异常中间件需要放到所有中间件的最后
  4. 异常中间件只能捕获回调函数中的异常.也就是get, post这些监控函数的异常. 在全局的异常,和get里的promise里的异常, 异常中间件获取不到.
const express= require("express");

//  创建
const app = express();
const port = 3000;


// 前置中间件
app.use((req, res, next)=>{
    console.log("before middle ware");
    next();
})

// 监控. 处理 localhost:3000的请求.
app.get("/", (req, res, next)=>{
    console.log("get");
    res.send("<div style='color: red'>111</div")
    next();
})

// 路由中间件. 处理 localhost:3000/test 的中间件
app.use("/test", (req, res, next)=>{
    console.log("test middle ware");
    res.send("test");
    next();
})

// 路由中间件 处理 localhost:3000/error 的中间件
app.use("/error", (req, res, next)=>{
    console.log("error middle ware");
    throw(new Error("报错"))
    next();
})

// 全局错误处理中间件, 有报错, 比如console.error, 就会来到这里.
app.use((error, req, res, next) =>{
    console.log("error middle ware", error.message);
    next();
})

// 后置中间件
app.use((req, res, next) =>{
    console.log("after middle ware");
})


// 启动
app.listen(port, ()=>{
    console.log("express启动", port);
})

6. express的报错异常的捕获.

  1. 在app.get/post里的回调的异常可以用app.use((err, req, res, next))捕获.
  2. 全局异常捕获用process.on("uncaughtException",()=>{})
  3. app.get/post的promise里的异常用 proces.on("unhandledRejection",()=>{}) ;
  4. process是node的进程对象, 控制进程的各个生命周期, 事件监控.
// 全局异常捕获.
process.on("uncaughtException", (error)=>{
    console.log("uncaughtException", error);
})
// 捕获promise中的报错.
process.on("unhandledRejection", (err)=>{
    console.log("unhandledRejection", err);
})

7. 设置服务器的静态文件.

服务器的路径一般都是走的路由监控请求. 但是有的文件是需要纯粹查看的文件, 比如在浏览器直接查看图片, pdf文件, txt文件, 视频文件, 音频文件等.
这就是服务器的静态文件, 指能直接查看静态文件的路径.


// 根目录下单 "./static"文件夹中的文件就可以通过 localhost:8080/static/xxx来直接查看了.
// 比如 "./static/a.pdf"的地址就是 localhost:8080/static/a.pdf .
app.use("/static", express.static("static"))

8. https协议的服务器.

配置https服务,需要购买https证书, 放到根目录的https下, xxx.key是私钥, xxx.pem是公钥.
我没买, 暂且先这样.

const https = require('https');
const httpsPort = 443;
const options = {
    key: fs.readFileSync("./https/xyz.key"), //私钥
    cert: fs.readFileSync("./https/xyz.pem"), // 公钥
};
const httpsServer = https.createServer(options, app);
httpsServer.listen(httpsPort, ()=>{
    console.log("https 服务启动成功", httpsServer);
})

vue-cli的实现

vue2-elm-master\build\dev-server.js 文件做的就是vue-cli做的内容. 可以看看,我写了注释.

9. webpackdevMiddleware webpack的开发服务中间件

  1. webpack-dev-server是webpack的开发模式的服务器, 启动后, 会将源码打包,并放到内存中, 可以通过浏览器地址访问
  2. Server.js. webpackdevMiddleware是webpackdevServer中专门来处理打包的具体位置的, 打包的文件输出路径从dist改成服务器内容路径, 不用每次都实际创建文件, 而是输出到内存里, 请求路径 localhost:8080的目标地址也指向内存路径.
// webpack-dev-server -> Server.js
var webpackDevMiddleware = require("webpack-dev-middleware");
this.middleware = webpackDevMiddleware(compiler, options);
  1. middleare.js, 通过Shared函数来处理保存路径问题, 在执行Shared函数时
    1. 把context的fs替换了. fs里面应该有 inputFileSystem输入文件的地址 , outputFileSystem文件输出路径, build时是文件的输出路径,在这里改成了内存地址.
// middleare.js
var Shared = require("./lib/Shared");

// 在这一步就直接把context的fs替换了. fs里面应该有 inputFileSystem , outputFileSystem文件输出路径, 一般
var shared = Shared(context);
shared.handleRequest(filename, processRequest, req);
webpackDevMiddleware.waitUntilValid = shared.waitUntilValid;
webpackDevMiddleware.invalidate = shared.invalidate;
webpackDevMiddleware.close = shared.close;
  1. Shared.js. share.setFs(context.compiler);就是改变fs(outputFileSystem)的操作. 通过 MemoryFileSystem 生成内存地址.
var MemoryFileSystem = require("memory-fs");
share.setFs(context.compiler);

// 修改 webpack的fs, fileSystem, 修改webpack默认的文件夹路径, 包括输出路径, 服务器请求路径
		setFs: function(compiler) {
			if(typeof compiler.outputPath === "string" && !pathIsAbsolute.posix(compiler.outputPath) && !pathIsAbsolute.win32(compiler.outputPath)) {
				throw new Error("`output.path` needs to be an absolute path or `/`.");
			}

			// store our files in memory
			var fs;
			var isMemoryFs = !compiler.compilers && compiler.outputFileSystem instanceof MemoryFileSystem;
			if(isMemoryFs) {
				fs = compiler.outputFileSystem;
			} else {
				fs = compiler.outputFileSystem = new MemoryFileSystem();
			}
			context.fs = fs;
		},
  1. memory-fs.js , 在这里是实际将文件内容保存到内存的地方. 创建的MemoryFileSystem对象会替换fs,
    1. node有fs对象, 可以创建,读取,修改文件,创建,读取,修改文件夹.const fs = require("fs")
    2. webpack有this.fs是对node的fs的封装(大概,不确定), 应该是加了前后置的操作, this.fs也可以 创建,读取,修改文件,创建,读取,修改文件夹
    3. 在开发模式中,MemoryFileSystem对象创建的实例机会替换掉webpack的fs, 使webpack在开发模式中,创建,读取修改文件时,不用node的fs而是用MemoryFileSystem
    4. MemoryFileSystem对象有node的fs对象上的所有方法,比如readFile,readFileSync, writeFile, rmdirSync可以保证替换掉fs后this.fs还能正常运行.
    5. 所谓的把文件保存到内存中就是指, 把文件内容保存到MemoryFileSystem的data属性里.data的结构大概是下面的情况.
    6. 当使用this.fs.readFile(url, (content)=>{}) 方法获取文件内容时, 通过data[url]获取到文件对象, 可以查看文件大小,文件内容. 将文件内容content返回.
    7. 当使用this.fs.rmdirSync(url)时, 通过查找data下面的路径, 把相关路径对象删除即可.
    8. 所谓的把文件内容放到内存里就是把打包好的文件的字符串内容, 创建一个对象,保存起来.即可.
// memory-fs.js
// MemoryFileSystem对象
["stat", "readdir", "mkdirp", "mkdir", "rmdir", "unlink", "readlink"].forEach(function(fn) {
	MemoryFileSystem.prototype[fn] = function(path, callback) {
		try {
			var result = this[fn + "Sync"](path);
		} catch(e) {
			return callback(e);
		}
		return callback(null, result);
	};
});
MemoryFileSystem.prototype.exists = function(path, callback) {
	return callback(this.existsSync(path));
}
MemoryFileSystem.prototype.readFile = function(path, optArg, callback) {};
MemoryFileSystem.prototype.readFileSync = function(_path, encoding) {};
MemoryFileSystem.prototype.writeFile = function (path, content, encoding, callback) {};
MemoryFileSystem.prototype.rmdirSync = function(_path) {
	return this._remove(_path, "Directory", isDir);
};
// MemoryFileSystem的data的结构, middleware中的内容是 Buffer形式存在, content.toString() => 就是要用的字符串
{
    G: {
        "webpack-learn": {
            "vue2-elm-master": {
                "dist": {
                    src: {
                        "index.js": Buffer(xxxxx)
                    },
                    "index.html": Buffer(xxxxx)
                }
            }
        }
    }
}

  1. 浏览器请求文件的过程

middleware.js

  1. 浏览器发出请求"http://localhost:8000/", 由于地址末尾没有文件名, 使用默认文件名即"http://localhost:8000/index.html"
  2. middleware监控8000端口,获取到请求, 拿到请求地址url"index.html"
  3. 根据webpack中的compiler保存的项目绝对路径G\webpack-learn\vue2-elm-master, 和 "publicPath"项目相对路径, 和文件名"index.html",
    拼接处index.html的绝对路径 path =G\webpack-learn\vue2-elm-master\dist\index.html,
var filename = getFilenameFromUrl(context.options.publicPath, context.compiler, req.url);
  1. 用''分割path,获取index.html的路径数组['G', 'webpack-learn','vue2-elm-master', 'dist', 'index.html'],
  2. 在用这个路径数组,从 MemoryFileSystem的data(上一段落)里找对应文件, 拿到buffer后, Buffer.toString()即可获得index.html的文件内容
  3. 设置header, 比如content-type, content-length, 等.
res.setHeader("Content-Type", contentType);
res.setHeader("Content-Length", content.length);
  1. contentType的获取, contentType是根据文件名获取的, 在mime.js中维护了一个types.json.里面保存了所有文件后缀对应的contentType,
{
    // contentType: [文件后缀]
   "text/html":["html","htm","shtml"],
   "application/json":["json","map"],
}
  1. 返回内容.
res.statusCode = res.statusCode || 200;
if(res.send) res.send(content);

10. webpack-dev-server的middleware处理浏览器请求总结.

  1. express监控服务器请求有两种方式,
    1. 监控请求 app.get("/index");
    2. 中间件监控 app.use(()=>{}); 中间件监控,不设置监控路径的话可以处理所有的请求
  2. webpack-dev-server就是通过中间件, 监控到浏览器所有的请求.
  3. 修改webpack的fs为MemoryFileSystem,并将构建打包结构全部存储到内存中(用路径方式保存到一个对象里,文件内容就是属性值,buffer格式)
  4. 实现请求中间件, 用于处理所有资源请求, 并到内存中查询文件返回.

hmr热更新

html5服务器发送事件EventSource.

  1. HTML5 服务器发送事件(server-sent event)允许网页获得来自服务器的更新。
  2. h5方面: new EventSource("getList")会发送一个请求, 请求发送后, 不会断开, 而是每隔一段时间(比如10秒),服务器就会推送内容回来
    h5可以在 source.onmessage事件中获取到推送内容.
// 发送 localhost:8000/getList请求.
var source=new EventSource("getList");
source.onmessage=function(event){
    document.getElementById("result").innerHTML+=event.data + "<br>";
};
  1. 服务器方面, 针对getList请求,需要设置header: {'Content-Type':'text/event-stream', 'Cache-Control': 'no-cache', 'Connection':'keep-alive'}, 然后每隔10s推送一部分内容.h5方面即可获取.
const http = require('http');
const fs = require('fs');
http.createServer((req, res) => {
  if (req.url === '/getList') {
    // 设置header
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');
    res.flushHeaders();

    setInterval(() => {
        // 此处格式不对, 浏览器无法无法获取发送内容.
      res.write(`data: ${new Date().toISOString()}\n`);
    }, 1000*10);
  }
}).listen(3001, () => {
  console.log('Server is running on port 3001');
});
  1. HMR热更新就是用webpack-hot-middleware开了一个中间件, 设置了一个EventSource请求. 每当监控到源文件有变化时
    就重新打包一次, 或者定时更新(比如5秒), 然后通过EventSource请求把变化内容发送给客户端浏览器.

注意这是webpack@1的实现方式, 后续的webpack的实现方式暂时未知.
以下是webpack的EventSource请求例子
EventSource的请求信息
EventSource的响应信息

EventSource事件的发送方式

HMR 热更新的实现机制

  1. 需要客户端和服务端同事配合支线( HotModuleReplacementPlugin 和 webpack-hot-middleware 连动使用)
  2. 客户端和服务端双向通信机制复杂.

webpack.dev.conf.js中

  1. 在webpack的plugins里添加hotModuleReplacementPlugin, new webpack.HotModuleReplacementPlugin()
  2. webpack的entry属性可以是数组.entry: ['app.js', './build/dev-client']这两块代码都会被打包到index.js中. 挨个执行.
// 给entry添加入口, './build/dev-client'是把 'webpack-hot-middleware/client'里的函数加入到入口中.
Object.keys(baseWebpackConfig.entry).forEach(function(name) {
    baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})
  1. './build/dev-client'是把 'webpack-hot-middleware/client'里的函数加入到entry中.

webpack-hot-middleware/client.js

  1. 在这里初始化了EventSource的请求地址, 超时时间, 频率等, 也就是options.
  2. processMessage根据 webpack-hot-middleware 传过来的obj.action的值,做响应处理
  3. 如果action是"sync", 则用processUpdate更新内容.
  4. 由于client.js已经被注入到index.js即项目的入口文件里, 所以上述代码会在浏览器中执行. 所以才能动态的直接变更响应dom结构.
// 基础配置
var options = {
  path: '/__webpack_hmr',
  timeout: 20 * 1000,
  overlay: true,
  reload: false,
  log: true,
  warn: true,
  name: '',
  autoConnect: true,
  overlayStyles: {},
  overlayWarnings: false,
  ansiColors: {},
};

function processMessage(obj) {
  switch (obj.action) {
    case 'building':
        // log
      break;
    case 'built':
    // log fall through
    case 'sync':
        // 判断是否要更新
      if (applyUpdate) {
        // 更新内容
        processUpdate(obj.hash, obj.modules, options);
      }
      break;
    default:
      if (customHandler) {
        customHandler(obj);
      }
  }
}


connect-history-api-fallback

connect-history-api-fallback是一个用于处理单页应用(SPA)路由问题的中间件,它主要用于解决在使用HTML5 History API时,用户直接访问子页面的问题。

在单页应用中,我们通常使用HTML5的History API来管理URL的变化,而不是通过服务器重新加载整个页面。然而,当用户直接访问一个子页面的URL时,如果没有相应的路由处理逻辑,浏览器会尝试向服务器请求该资源,但服务器可能无法找到对应的资源,从而导致404错误。

为了解决这个问题,我们可以使用connect-history-api-fallback中间件。它会拦截所有非API请求,并重定向到指定的入口文件(通常是index.html)。这样,无论用户访问哪个URL,都会被重定向到index.html,然后由前端路由库(如React Router、Vue Router等)接管并正确渲染相应的组件。

const express = require('express');
const history = require('connect-history-api-fallback');
const app = express();
app.use(history());
// 其他中间件和路由配置
app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});
app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

标签:vue,cli,res,app,中间件,js,webpack,fs,原理
From: https://www.cnblogs.com/bridge7839/p/18572998

相关文章

  • vue2升级vue3
    vue2升级vue3针对../vue3-elm-master项目升级做的解析,该项目目前是webpack@1,vue@2.1.vue2的缺点,vue3的优势.vue2的劣势vue2的组件代码复用是用mixin,容易出现命名冲突,且无法解决业务逻辑的复用.vue2的mixin代码复用不好处理,本质是把mixin里的属性和方法直接赋值......
  • vue3手搓固钉组件
    #创作灵感    今天用固钉组件时发现element-ui的固钉会阻止移动端移动事件,我思来想去决定自己写一个固定组件得了实现思路        获取钉子的ref实例并监听window滚动事件,当window.scrollY等于钉子的offsetHeight,添加position:fixed和top:0到钉子上,就能强......
  • Z2400017基于Java+mysql+SpringBoot+Vue实现的社区博客系统 源码 PPT 配置 文档
    社区博客系统1.项目概述2.系统功能3.技术栈及运行环境4.界面展示5.源码获取1.项目概述社区博客系统是一个基于SpringBoot和Vue.js构建的全栈Web应用程序,旨在为用户提供一个功能丰富、互动性强且易于管理的博客平台。该系统结合了现代Web开发中最先进的技术栈,确保了......
  • vue3-路由Router
    基本使用(与vue2语法有差异)安装vue-router,vue3需要使用vue-router的4版本npmivue-router@4编写路由文件//引入createRouterimport{createRouter,createWebHistory}from"vue-router";//引入组件importUserInfofrom"@/components/UserInfo.vue";//创......
  • 深入理解Vue 3的Composition API和<script setup>语法糖
    引言Vue3引入了CompositionAPI,这是一个全新的API,旨在解决大型组件中逻辑复用和代码组织的问题。同时,Vue3还引入了<scriptsetup>语法糖,使得使用CompositionAPI更加简洁和直观。本文将深入探讨CompositionAPI和<scriptsetup>的核心概念,并通过实际代码示例展示它们的使用......
  • 基于SpringBoot+Vue的网上租赁管理系统设计与实现毕设(文档+源码)
    目录一、项目介绍二、开发环境三、功能介绍四、核心代码五、效果图六、源码获取:         大家好呀,我是一个混迹在java圈的码农。今天要和大家分享的是一款基于SpringBoot+Vue的网上租赁管理系统,项目源码请点击文章末尾联系我哦~目前有各类成品毕设JavaWeb......
  • # vue 实现关键字高亮效果
    vue实现关键字高亮效果这是啥子意思呢,就是类似于百度搜索,根据关键词搜索结果,搜索结果中,与关键词相同的字显示红色,仅此而已,没有什么大的功能。简单写一下demo。环境我使用的是vue3+ts的语法来写,其实一个样儿,关键代码js、ts都可以,就一个方法,调用一下就可以了。<templat......
  • vue-admin-template学习
    vue-admin-template权限篇参考网址-https://juejin.cn/post/6844903478880370701思路:-不同权限对应不同的路由-侧边栏也需要根据不同的权限,异步生成实现逻辑-用户名和密码校验是否正确 -正确返回token,存储到cookie(记住用户登录状态) -带着token向后端......
  • http代理IP技术的原理?
    HTTP代理IP技术是一种通过代理服务器转发HTTP请求和响应的技术,可以实现隐藏客户端IP地址、加速访问、突破网络限制等功能。其原理如下:1,客户端发起HTTP请求当客户端(例如浏览器)要访问一个网站时,它会向代理服务器发送HTTP请求,这个请求包含了要访问的网站的URL、请求方法(GET、PO......
  • Vue的ref与$refs
    一、Vue的ref与$refs   通过this.$refs获取对应的DOM元素对应代码  <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0......