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

【前端工程化】-【vue2-ele项目升级】

文章目录

  • 大型前端项目架构升级
    • 项目启动+构建命令分析
    • 开发模式构建源码分析
      • vue-cli在开发模式下为什么要启动express服务
    • http服务原理讲解+express服务快速搭建
    • express核心概念middleware深入讲解
    • 异常中间件和异常捕获机制
    • express启动https服务+静态资源服务搭建实战
  • 深入解析webpack
    • webpack-dev-middleware实例化源码解析
  • vue2-elm项目工程话升级
    • vue3与vue2对比
    • 全家桶依赖升级+项目启动脚本升级
    • 编译错误解决
    • vue-router和vuex语法升级
    • 图片链接升级
    • vue3语法升级
  • 项目打包构建优化
    • 构建速度测量插件speed-measure-webpack-plugin
    • 构建体积分析webpack-bundle-analyzer
    • 构建性能优化值多进程thread-loader
    • webpack分包策略
    • 利用缓存提升二次构建速度
    • 图片压缩

大型前端项目架构升级

项目源码

项目启动+构建命令分析

项目拉下来后安装依赖:npm install,然后执行npm run dev启动项目,查看package.json的命令发现命令执行涉及两个文件:dev-server.js和build.js,我们先看前者,他是一个node写的文件,在vue-cli早期,他是直接将构建源码写在项目的build、config两个文件夹下,src是项目的源码,所以想优化构建过程可以直接修改dev-server.js,但这种方式会让项目的体积变得很大,下载项目的时候会下载很多无关的源码下来,现在的vue-cli是不存在这两部分源码的,config会有一些,但是build是完全并入到vue-cli中,并提供了更多新的功能,比如插件等扩展功能

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

开发模式构建源码分析

vue-cli在开发模式下为什么要启动express服务

一、解决线上部署后的资源路径问题:线上部署时,通常会将源码放在一个服务器上,但不会把源码放在服务器的根目录下,因为服务器上不会只放一个项目,所以我们的源码是放在服务器的某个文件夹下,假如这个文件夹是vue2-elm,浏览器访问的url就应该是:http://www.xx.com/vue2-elm/index.html,我们把vue2-elm称为publicPath,在vue.config.js中配置publicPath:

module.exports = {publicPath:'vue2-elm'
}

后执行npm run dev启动项目,会发现本地访问的资源为:
请添加图片描述
和线上环境一样,会多一级/vue2-elm。但是执行npm run build后,打开dist/index.html会出现报错,因为所有的资源都带上了publicPath,这就是为什么打包后会找不到资源路径,所以这就需要我们把源码放在服务器上的vue2-elm文件夹下,所以在本地配置了publicPath就启动不起来了,而使用express服务器就可以访问到
请添加图片描述
二、解决history模式下的url fallback问题,也就是在用户遇到404报错的时候,要让用户返回index.html,要做这个配置,而express服务器启动后,他可以帮你做这个配置

http服务原理讲解+express服务快速搭建

express服务主要解决的问题:让node可以写web服务器。
用户通过浏览器访问某个域名时,会去找到某台服务器,这台服务器必须启动一个web服务,express就是这台服务器上的web服务,假如浏览器访问的是http://www.imooc.com:80,那么express就要监听80端口,浏览器对80端口请求的所有报文信息都会到express服务,express服务会处理请求并返回结果。在vue-cli中启动了项目,浏览器访问的就是http://localhost:8080/test,这时express服务监听的就是8080端口,express从当前文件夹中找到test目录下的index.html返回,这个index.html可能会请求其他的文件,比如js、css等,也是由express服务响应。
创建express-test文件夹,在其中打开终端执行npm init -y初始化项目,然后安装express:npm install -S express,在app.js中写一个express服务器

// 1. 创建服务
const express = require('express')
const app = express()
// 2. 拦截路由
// 通过app.get拦截get请求
app.get(/',function(req,res){// 只要请求的url是/,则执行这个回调函数。req是请求头内容,res是响应内容res.send('<html><body><div style="color:red">111</div></body></html>')
})
// 3. 启动服务
const port = 8080
app.listen(port,function(){console.log('服务启动成功,端口号:',port)
})

在终端执行node app.js即可启动express服务,本地访问http://localhost:8080就会得到响应

express核心概念middleware深入讲解

中间件:用来处理请求的业务逻辑

// 1. 创建服务
const express = require('express')
const app = express()// 前置中间件
// 全局中间件:中间件第一个参数为回调函数时,针对所有请求生效
app.use(function(req, res, next) {console.log("before middleware")next()//继续执行后面的中间件
})// 路由中间件:第一个参数为匹配路由,第二个参数为回调函数
app.use('/test',function(req, res, next) {console.log("after middleware")res.send('test')next()//继续执行后面的中间件
})// 2. 拦截路由(本质上也是个路由中间件)
// 通过app.get拦截get请求
app.get(/',function(req,res){// 只要请求的url是/,则执行这个回调函数。req是请求头内容,res是响应内容res.send('<html><body><div style="color:red">111</div></body></html>')next()//继续执行后面的中间件,如果没有next()不会执行下一个中间件
})// 后置中间件
app.use(function(req, res, next) {console.log("after middleware")
})// 3. 启动服务
const port = 8080
app.listen(port,function(){console.log('服务启动成功,端口号:',port)
})

异常中间件和异常捕获机制

// 1. 创建服务
const express = require('express')
const app = express()// 路由中间件:第一个参数为匹配路由,第二个参数为回调函数
app.use('/test',function(req, res, next) {console.log("after middleware")next()//继续执行后面的代码
})// 2. 拦截路由(本质上也是个路由中间件)
// 通过app.get拦截get请求
app.get(/',function(req,res){// 只要请求的url是/,则执行这个回调函数。req是请求头内容,res是响应内容res.send('<html><body><div style="color:red">111</div></body></html>')next()//继续执行后面的代码,否则不会执行后置中间件
})app.use(function(req, res, next) {console.log("after middleware")throw new Error("error message")
})app.use(function(req, res, next) {console.log("after middleware2")new Promise((resolve,reject)=>{console.log('promise')resolve()}).then(() => {console.log('then')// 异常中间件只能捕获回调函数中的异常,promise异常是捕获不到的,但是node能捕获到,// 	为了不让node捕获到,我们自己捕获自己处理,可以使用process.on(见下)throw new Error("error message")})
})// 异常中间件:回调函数包含了4个参数
// 1. 全局只包含一个,如果有多个,那也只有第一个生效
// 2. 异常中间件可以传递给普通中间件
// 3. 异常中间件需要放在所有中间件的最后,从而能捕获到所有异常
// 4. 异常中间件只能捕获回调函数中的异常
app.use(function(err, req, res, next) {console.error("error!", err.message)next()// 异常中间件可以传递给普通中间件,打印完err.message后打印err2!
})app.use(function(req, res, next) {console.log("err2!")
})// 全局异常捕获,主要用来捕获非回调函数的异常
process.on('uncaughtException', function(err){console.error("uncaughtException", err.message)
})// 全局promise异常捕获
process.on('unhandleRejection', function(err){console.error("unhandleRejection", err.message)// 打印error message
})// 3. 启动服务
const port = 8080
app.listen(port,function(){console.log('服务启动成功,端口号:',port)
})

express启动https服务+静态资源服务搭建实战

新建/https/xxx.key、/https/xxx.perm用于保存ssl的公钥和私钥

// 1. 创建服务
const express = require('express')
const https = require('https')
const fs = require('fs')
const app = express()// 2. 拦截路由
// 通过app.get拦截get请求
app.get(/',function(req,res){// 只要请求的url是/,则执行这个回调函数。req是请求头内容,res是响应内容res.send('<html><body><div style="color:red">111</div></body></html>')
})// 3. 启动服务
const port = 8080
const httpsPort = 443
const options = {key:fs.readFileSync('./https/xxx.key'),// 私钥cert:fs.readFileSync('./https/xxx.pem')// 公钥
}
// 启动http服务
app.listen(port,function(){console.log('http服务启动成功,端口号:',port)
})
// 启动https服务
const httpsServer = https.createServer(options, app)
app.listen(httpsPort,function(){console.log('https服务启动成功,端口号:',httpsPort)
})

如果你本地有个静态资源文件,你想让他被远程访问到,可以将其静态资源部署到服务器上。新建static/index.html、static/index.js,index.html中使用script标签引入index.js,index.js代码:console.log('index.js')

// 1. 创建服务
const express = require('express')
const app = express()// 前置中间件
app.use(function(req, res, next) {console.log("before middleware")next()//继续执行后面的代码
})// 浏览器访问/static时,将/static下所有的资源转换为静态资源
// 同构部署:将前后端代码同构在一起
app.use('/static',express.static('./static'))// 2. 拦截路由
// 通过app.get拦截get请求
app.get(/',function(req,res){// 只要请求的url是/,则执行这个回调函数。req是请求头内容,res是响应内容res.send('<html><body><div style="color:red">111</div></body></html>')
})
// 3. 启动服务
const port = 8080
app.listen(port,function(){console.log('服务启动成功,端口号:',port)
})

深入解析webpack

webpack-dev-middleware实例化源码解析

vue2-elm项目工程话升级

vue3与vue2对比

mixins本质是代码片段复用,mixins将componentA和componentB的通用代码,比如变量b和方法handleB在componentA和componentB中都注入进去,那可以建一个mixins将代码片段拷贝进来,相当于你把原本应该写在componentA和componentB的代码写在了一个公共的地方进行代码复用,这就可能出现命名冲突,假如componentA中也有变量b,那后调用的会覆盖先调用的,并且minxins不能对整个业务逻辑进行复用,因为他是代码片段,所以他可能存在很多依赖,比如handleB()可能会去调handleA(),哪怕handleA()在mixins中不存在,而vue3通过hooks彻底解决了这两个问题,hooks把一个业务逻辑需要的所有状态和方法封装在一起,实现业务逻辑的隔离,比如在componentA和componentB中都会用到data,hooks是一个function,返回一个闭包,从闭包中调用data和getData(),
请添加图片描述
因为树摇需要export function或者export const这种方式引用,所有的方法挂在this上我们没有办法做树摇,导致vue2打出来的包比较大,vue3很多优化点都是针对这个点来优化的
请添加图片描述

全家桶依赖升级+项目启动脚本升级

将package.json中vue、vue-router、vuex的版本号
请添加图片描述
配置启动、打包脚本:

"scripts":{"serve":"vue-cli-service serve","build":"vue-cli-service build"
}

编译错误解决

项目中有很多找不到某个路径问题,原本别名配置在webpack.base.conf.js中
请添加图片描述
但是从官方文档可以看到,别名应该配置在vue.config.js的configureWebpack中
请添加图片描述
删除webpack.base.conf.js中的别名配置,将其添加到vue.config.js并修改路径:
请添加图片描述

vue-router和vuex语法升级

vue3虽然兼容vue2,但是只是兼容部分特性,项目结构(尤其是vue全家桶这一块)是完全没法使用的,所以本节进行vue2到vue3的升级
一、vuex的移植
请添加图片描述
请添加图片描述
二、vue-router移植

  1. 修改入口文件router的使用:
    请添加图片描述
  2. 更新vue-router配置:修改项目中vue-router的使用,首先是引用路由的方式变了,vue3只用按需引入createRouter和createWebHistory,router的注册也和vue2不一样,vue2需要引入整个App,其次,vue3使用import引入组件,vue2使用require
    请添加图片描述

图片链接升级

项目启动后,定位页面的城市信息样式没有生效
请添加图片描述
查看代码发现:

.citylistul{li{float: left;text-align: center;color: $blue;border-bottom: 0.025rem solid $bc;border-right: 0.025rem solid $bc;@include wh(25%, 1.75rem);@include font(0.6rem, 1.75rem);}li:nth-of-type(4n){border-right: none;}}
<ul class="citylistul clear"><router-link  tag="li" v-for="item in hotcity" :to="'/city/' + item.id" :key="item.id">{{item.name}}</router-link>
</ul>

在vue2中,给router-link设置了tag="li",路由会被编译/渲染为li标签,但是在vue3中这是不生效的,通过页面截图可以看到ul标签中展示的是a标签,所以将样式改为:

.citylistul{a{float: left;text-align: center;color: $blue;border-bottom: 0.025rem solid $bc;border-right: 0.025rem solid $bc;@include wh(25%, 1.75rem);@include font(0.6rem, 1.75rem);}a:nth-of-type(4n){border-right: none;}}

vue3语法升级

  1. data移植,使用ref定义普通变量,使用reactive定义对象,变量的赋值和使用都使用.value
    请添加图片描述
  2. method移植
    请添加图片描述
  3. 生命周期函数移植
    请添加图片描述
  4. computed移植
  5. 请添加图片描述

项目打包构建优化

构建性能优化方法:
一、查找并诊断性能瓶颈

  1. 构建速度分析:影响构建性能和开发效率
  2. 构建体积分析:影响页面访问性能

二、构建性能优化常用方法

  1. 通过多进程加快构建速度
  2. 通过分包减小构建目标容量
  3. 减少构建目标加快构建速度

构建速度测量插件speed-measure-webpack-plugin

安装:npm i -D speed-measure-webpack-plugin
在vue.config.js中配置:

const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin({disable: !(processs.env.MEASURE === 'true'),// 是否开启这个插件outputFormat: 'humanVerbose',// 会将构建过程拆分的更细致// 其余配置项:// outputTarget:输入输出流,基本不用改了// pluginNames:指定实际要测量的plugin的name// loaderTopFiles:打印出前10个loader的文件// compareLoadersBuild:选择输出路径
})
module.export = {configureWebpack: amp.wrap({resolve:{alias:{……}}})
}

构建时,可以在控制台看到各个插件和loader所花费的时间

构建体积分析webpack-bundle-analyzer

安装:npm i webpack-bundle-analyzer
请添加图片描述
执行npm run serve后,会在浏览器另开一个页签展示打包结果
请添加图片描述
通过控制台的network可以看到,最终打包出来生成的就是上图两个js,体积分别为3.4m和4.8m,代码没有压缩,且把所有的源码都打包到了app.js中,他还会把components下面的common也打包进来,还把plugin中的swiper也打包进来了,以及其他的(像图片)全打包进来了。也可以看到chunk_vendors.js中,也把所有的node_modules打包进来了.

new BundleAnalyzerPlugin({// 可选参数analyzerMode:'static',// 分析文件是一个静态资源,保存在/dist/report.html。值为server表示新起一个服务展示分析结果,json:输出一个json文件,disabled:不做任何输出// analyzerHost:部署的ip,默认为127.0.0.1// analyzerPort:部署的端口号,默认为8888// reportFilename:输出文件名,默认为report.html// reportTitle:输出的标题// openAnalyzer:默认是否在浏览器中自动打开// staticFilename:输出的json文件的名称// excludeAssets:排除哪些资源// logLevel:指定log级别
})

构建性能优化值多进程thread-loader

请添加图片描述
官方给的使用注意事项:
请添加图片描述
本项目的测试数据:
请添加图片描述
在vue-cli-server中已经内置了这个插件,可以通过parallel开启
请添加图片描述

module.exports = {parallel: true,
}

webpack分包策略

请添加图片描述
entry:让哪些库来分包,是一个对象,可以在里面定义多个键值对,值是一个数组,package.json中dependencies的内容都可以写在数组中,我们这里先拆一个vue的包,将vue的生态都打到这个包里

利用缓存提升二次构建速度

请添加图片描述
将打包的中间文件放到本地磁盘或者内存中,第二次构建时,可以使用第一次构建的一些缓存文件,其中不会变动的部分我们会直接拿来打包构建,从而提高构建效率
cache.cacheDirectory:指定缓存目录
请添加图片描述
cache.name:指定缓存名称
请添加图片描述
cache.cacheLocation:cache.cacheDirectory和cache.name的结合,帮助我们直接找到最终的缓存路径
请添加图片描述

图片压缩

请添加图片描述
请添加图片描述


http://www.mrgr.cn/news/98320.html

相关文章:

  • 深度学习ResNet模型提取影响特征
  • 【数据结构_6下篇】有关链表的oj题
  • C语言打印的坑
  • 【玩转全栈】—— Django 连接 vue3 保姆级教程,前后端分离式项目2025年4月最新!!!
  • 个人博客系统后端 - 注册登录功能实现指南
  • 行星际激波在日球层中的传播:Propagation of Interplanetary Shocks in the Heliosphere (第二部分)
  • linux多线(进)程编程——(5)虚拟内存与内存映射
  • 【Java学习笔记】Java第一课,梦开始的地方!!!
  • centos7系统搭建nagios监控
  • SQL 解析 with as dual sysdate level
  • 剑指Offer(数据结构与算法面试题精讲)C++版——day9
  • Day30笔记-综合项目: 购物车
  • CMD命令行笔记
  • Pytorch深度学习框架60天进阶学习计划 - 第41天:生成对抗网络进阶(三)
  • 【随手笔记】QT避坑一(串口readyRead信号不产生)
  • 【3GPP核心网】【5G】精讲5G系统的策略和计费控制框架
  • Linux:39内核与用户--信号-lesson28(待)-未完多个子进程处
  • 分布式日志治理:Log4j2自定义Appender写日志到RocketMQ
  • 网络机顶盒常见问题全解析:从安装到故障排除
  • 【第十三届“泰迪杯”数据挖掘挑战赛】【2025泰迪杯】【论文篇+改进】A题解题全流程(持续更新)