vue2 项目升级到vue3之后`npm run build`执行两遍打包

vuecli3 执行 npm run build 打包命令报错:TypeError: file.split is not a function 这样就导致了报错,懒加载多的时候在开启,通过合并的方式处理 chunk,以减少请求数,一个的时候没必要。我排查了一下,模拟代码如下:在打包的时候用了。 阅读详情

vue2 项目升级到vue3之后npm run build执行两遍打包

实际是在 @vue/cli-service升级到5.0版本之后出现的问题

先说解决方法

两种办法

  1. 执行 build 的时候加一个 --no-module
 vue-cli-service build --no-module 
  1. 修改 browserslist,一般在 package.json 中或者单独的 .browserslistrc 文件中,添加一个 not ie 11

package.json

 "browserslist": [
     "> 1%",
     "last 2 versions",
     "not dead",
     "not ie 11"
  ] 

.browserslistrc

 > 1%
 last 2 versions
 not dead
 not ie 11 

分析原因

通过执行 npm run build 的时候打印的日志可以发现两次打包之前都输出了不一样的日志

 Building legacy bundle for production... 
 Building module bundle for production... 

正常只执行一次的打包只会输出一种日志

 Building for production... 

然后我们根据日志输出的关键字在 @vue/cli-service 项目中查找一下,我们执行的是 build 命令,所以先看这个命令的文件 @vue/cli-service/lib/commands/build/index.js,搜索一下关键字 legacy bundle 会查找到第 116 行

 if (args.target === 'app') {
     const bundleTag = args.needsDifferentialLoading
       ? args.moduleBuild
         ? `module bundle `
         : `legacy bundle `
       : ``
     logWithSpinner(`Building ${bundleTag}for ${mode}...`)
   } 

发现当 args.needsDifferentialLoadingtrue 的时候就会出现打包两次所出现的日志,所以基本可以肯定问题出在这个上,继续找一下它的复制,往上查找,在67行发现了赋值语句

 args.needsDifferentialLoading = needsDifferentialLoading 

继续查找 needsDifferentialLoading 变量声明和赋值的地方,往上看就可以看到

 const { allProjectTargetsSupportModule } = require('../../util/targets')
 let needsDifferentialLoading = args.target === 'app' && args.module
 if (allProjectTargetsSupportModule) {
   log(
     `All browser targets in the browserslist configuration have supported ES module.\n` +
     `Therefore we don't build two separate bundles for differential loading.\n`
   )
   needsDifferentialLoading = false
 } 

needsDifferentialLoading 初始值如果 args.modulefalse 的话就是 false

在正常的项目开发中 arr.target 的值一定是 app,如果开发的是插件的话,那么一般在打包的时候会指定 --target lib

还有就是如果 allProjectTargetsSupportModule 这个值是true的话, needsDifferentialLoading 会被手动赋值成 false ,于是我们发现了两个可以让 needsDifferentialLoadingfalse 的方法

–no-module的原理

先查找 args.module 的复制,会发现没有直接的赋值,args是整个回调函数的参数,而且在下面还给 args中没有的部分值,附上了默认参数,第23行

 api.registerCommand('build', {
    description: 'build for production',
     usage: 'vue-cli-service build [options] [entry|pattern]',
     options: {
       // ...
       '--no-module': `build app without generating <script type="module"> chunks for modern browsers`,
       // ...
     }
   }, async (args, rawArgs) => {
     for (const key in defaults) {
       if (args[key] == null) {
         args[key] = defaults[key]
       }
     }
   // ...
 })
 // defaults 第一行
 const defaults = {
   clean: true,
   target: 'app',
   module: true,
   formats: 'commonjs,umd,umd-min'
 } 

可以看到 defaults 中给了 module 一个默认值true, 那怎么让 module 变成 false 呢,其实可以看到 options 中有一项 --no-module 的描述是: 构建应用程序,无需为现代浏览器生成< script type="module " >,到这里基本就能猜到了加个 --no-module 就可以把 module 赋值成 false 了,但猜到归猜到了,我们还是看一下具体的实现吧。

  1. package.json 中确定程序执行的入口 bin/vue-cli-service.js

     "bin": {
       "vue-cli-service": "bin/vue-cli-service.js"
     }, 
    
  2. bin/vue-cli-service.js 中通过 minimist 解析了参数,并创建了 Service 的实例,并调用了 run 方法,并传入了解析后的参数

    minimist 会把参数中以 --no- 开头的参数,解析为 false

    minimist/index.js

     if (/^--no-.+/.test(arg)) {
       var key = arg.match(/^--no-(.+)/)[1];
       setArg(key, false, arg);
     } 
    

    bin/vue-cli-service.js

     const Service = require('../lib/Service')
     const service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd())
     const rawArgv = process.argv.slice(2)
     const args = require('minimist')(rawArgv, {/*...*/})
     const command = args._[0]
     service.run(command, args, rawArgv) 
    
  3. Service 在实例化的时候,添加了内置的 plugin 其中就包括了 ./command/build 命令

    lib/Service.js

     module.exports = class Service {
       constructor (context, { plugins, pkg, inlineOptions, useBuiltIn } = {}) {
         // ...
         this.commands = {}
         this.plugins = this.resolvePlugins(plugins, useBuiltIn)
       }
        resolvePlugins(inlinePlugins, useBuiltIn) {
         const idToPlugin = (id, absolutePath) => ({
           id: id.replace(/^.//, 'built-in:'),
           apply: require(absolutePath || id)
         })
         let plugins
         const builtInPlugins = [
           './commands/build',
           // ...
         ].map((id) => idToPlugin(id))
     ​
         if (inlinePlugins) {
           // ...
         } else {
           const projectPlugins = // ...
           plugins = builtInPlugins.concat(projectPlugins)
         }
         const orderedPlugins = sortPlugins(plugins)
         return orderedPlugins
       }
     } 
    
  4. 执行了 service.run 方法,run 方法中调用了 init 方法,在 init 方法中初始化好了插件之后,用传入的参数调用对应的回调函数

     async run (name, args = {}, rawArgv = []) {
       // load env variables, load user config, apply plugins
       await this.init(mode)
       args._ = args._ || []
       let command = this.commands[name]
       if (!command || args.help || args.h) {
         command = this.commands.help
       }
       const { fn } = command
       return fn(args, rawArgv)
     }
     init() {
       // apply plugins.
       this.plugins.forEach(({ id, apply }) => {
         if (this.pluginsToSkip.has(id)) return
         apply(new PluginAPI(id, this), this.projectOptions)
       })
     } 
    

    为每一个插件创建了一个 PluginAPI 的实例,PluginAPI 提供了 registerCommand 方法,并把回调函数保存在了service.commands

     class PluginAPI {
        constructor (id, service) {
         this.id = id
         this.service = service
       }
       registerCommand (name, opts, fn) {
         if (typeof opts === 'function') {
           fn = opts
           opts = null
         }
         this.service.commands[name] = { fn, opts: opts || {} }
       }
     } 
    
  5. 至此 build 的回调函数就收到了解析后的参数 module: false

not ie 11

来看第二种解决方案的原理,只要从 ../../util/targets 中导入的allProjectTargetsSupportModule 值为 true,就可以了

 const { allProjectTargetsSupportModule } = require('../../util/targets')
 if (allProjectTargetsSupportModule) {
   needsDifferentialLoading = false
 } 

lib/util/targets.js

 const projectTargets = getTargets()
 const allModuleTargets = getTargets(
   { esmodules: true },
   { ignoreBrowserslistConfig: true }
 )
 const allProjectTargetsSupportModule = doAllTargetsSupportModule(projectTargets)
 function doAllTargetsSupportModule (targets) {
   const browserList = Object.keys(targets)
 ​
   return browserList.every(browserName => {
     if (!allModuleTargets[browserName]) {
       return false
     }
 ​
     return semver.gte(
       semver.coerce(targets[browserName]),
       semver.coerce(allModuleTargets[browserName])
     )
   })
 } 

getTargetsbabel 提供的方法,如果参数为空,返回 browserlists 查询的默认值,参考:babeljs.io/docs/en/bab…

传入 esmodules: true ,返回 github.com/babel/babel… 这个json文件中查询的结果.

doAllTargetsSupportModule 方法中对 browserListallModuleTargets进行了比较,如果 browserList 中有 allModuleTargets 不存在的属性,就返回 false 或者 browserList 中的版本号,比 allModuleTargets 小,也会返回 false

输出对比一下这两个对象

 // browserList
 {
   android: '98.0.0',
   chrome: '97.0.0',
   edge: '98.0.0',
   firefox: '96.0.0',
   ie: '11.0.0',
   ios: '14.5.0',
   opera: '82.0.0',
   safari: '15.2.0',
   samsung: '15.0.0'
 }
 // allModuleTargets
 {
   android: '61.0.0',
   chrome: '61.0.0',
   edge: '16.0.0',
   firefox: '60.0.0',
   ios: '10.3.0',
   node: '13.2.0',
   opera: '48.0.0',
   safari: '10.1.0',
   samsung: '8.2.0'
 } 

发现 browserListallModuleTargets 中多了一个 ie: 11.0.0 ,那我们只要配置 browserlists 让他没有 ie 这一项就可以,ie最后的版本就是 11了,所以加一个 not IE 11 就可以了。

参考资料

[1] babeljs.io/docs/en/bab…

[2] github.com/browserslis…

\

使用Electron把Vue项目打包成桌面客户端 目录预期目标项目准备安装electron-builder调试electron:serve打包electron:build客户端配置 预期目标 本文假定读者已经能够顺利开发Vue2.x)项目,并期望使用Electron把项目打包成桌面客户端的形式(Windows平台,64位)。 项目准备 确保现有的Vue项目已经能够正常运行并可通过npm run build命令进行编译部署,这里需要注意编译后的资源路径问题。 默认情况下,Vue CLI会假设应用是被部署在一个域名的根路径下,如果是被部署在一个子路径下,则需 阅读详情

相关推荐

【一】使用vue-cli创建vue3的helloworld项目

接下来我们创建 runoob-vue3-app 项目。按下回车键后就会进入安装,等候片刻即可完成安装。2、使用 vue create 命令创建项目。1、安装vue的环境。

码农从0到1 971

开发 bilibili 网站爬虫,大数据分析研究.rar

# BiliSpider # 简介 这里是UUPs爬虫主项目组,任何新技术均会首先应用在B站爬虫上。我们的目标是为每位up主的创作之路助力,主要体现在: 1. 数据获取与管理:利用爬虫技术获取全B站的视频与用户数据,并用维护数据库的方法管理它们,为创作者提供检索服务 2. 数据筛选与可视化:在海量数据中筛选出有效且感兴趣的信息,并研究将它们可视化的方法,为创作者提供创作素材 3. 数据分析与科研:根据获取到的有效数据,对B站推广机制,用户习惯等进行研究,为创作者提供推广经验

npm run build无法打包的可能原因 npm ERR! missing script: build

结果报错npm ERR! missing script: build,后来发现package.json中scripts参数为 "scripts": { "dev": "vue-cli-service serve", "build:prod": "vue-cli-service build", "build:stage": "vue-cli-service build --mode stag...

代码青春 9万+

Vue2.5】打包报错:ValidationError: Invalid options object.....

vue项目打包发生报错,显示ValidationError: Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.

DomCode 7671

vue3 打包时报错

vue3 打包时报错 (env) [root@VM-20-16-centos vue3shower]# npm run build > vue3shower@0.1.0 build > vue-cli-service build All browser targets in the browserslist configuration have supported ES module. Therefore we don't build two separate bundles for d

zhuxian2009的专栏 5654

CDN打包出现问题

根据错误信息,你的项目构建失败,原因是在 "E:\Frontend Basic Video Practice\10-vue\day10\major-events\src\main.js" 文件中存在两个错误,具体是 "Unexpected space before function parentheses",也就是函数括号前面出现了意外的空格。如果问题仍然存在,请检查其他错误信息,以便更好地理解问题的根本原因。这将自动修复在代码中可修复的 ESLint 错误。选项来修复可能可修复的错误。

qq_50680846的博客 1752

vue `npm run build`打包后的路由内组件不显示问题

今天做项目的时候,碰到一个问题 很匪夷所思: 项目做好后进行npm run build打包项目,结果,出现的问题如下图: 下图是页面结构:可知 中间的 <router-view></router-view>部分内容根本就没有显示!!!! 终于,经过仔细勘察,终于,被我知道了是什么原因 ~~~~~ 那是因为 mode:'history', 在 new Router({})...

qq_35457469的博客 2636

前端开发:Vue项目打包步骤

前端开发过程中,开发项目编码是必须的能力,但是开发之后的上线也是不可缺少的技能。而且现在的前端开发用的打包都是借助于三方插件来完成的,本篇博文就来分享一下前端项目打包的流程步骤,本案例以Vue来介绍,其他前端技术的打包步骤这里不再过多介绍。 Vue项目打包一般是基于webpack+vue-cli下的操作,具体的操作命令也很简单,只需要在终端里面输入打包命令:npm run build 回车即可进行打包。但是需要注意一点,那就是需要完整的vue-cli项目,通过vue init webpack初始化的项目

全沾软贱开发攻城狮 1万+

Vue项目打包到django部署

前言 Vue项目一般用于实现前端的单页面富应用,其打包后的文件可简单看作静态文件,所以可以通过Nginx部署,当然也可以通过django部署(毕竟本质上还是一个html文件及各CSS,JS文件的集合)。通过django部署还可以省去一些跨域的麻烦,不过前后端的耦合性就比完全分开部署的差一点。 测试所使用框架版本 django 3.06,VueCli 3.0 Vue打包 修改vue.config.js(VueCli3.0新建的项目里没有,要在项目目录新建一份) 修改为 module.exports = {

lymmurrain的博客 3899

vue项目打包以及优化

vue项目打包上线及优化 项目完成,我们会将项目进行上线,为了提升性能,我们往往会进行一些优化处理 vue项目打包 脚手架项目中有一个默认的打包命令,我们可以输入npm run bulid来对项目进行打包 打开终端,切换到项目根目录 输入命令:npm run build 会在当前项目的根目录下生成一个dist文件夹,里面就是打包后的文件 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NISVuSR5-1627475455629)(img\01-打包后的文件目录

Donnien的博客 2273

vue项目打包publicpath_vue项目打包到上线

vue项目打包到上线的基础做法总结1、开发环境到生产环境的转变项目开发结束之后,首先我们需要通知后端,获取一个线上的路径,之后将之前的开发路径切换为线上路径。打开项目中config文件夹里面的 dev.env.js 文件。'use strict'const merge = require('webpack-merge')const prodEnv = require('./prod.env')mo...

weixin_42467960的博客 1761

Vue脚手架2和脚手架3常用的命令

引言 不管是Vue脚手架2、还是脚手架3,对于一些细节的把握你了解吗? vue-cli工程中常用的npm命令有哪些? npm install:下载 node_modules 资源包的命令 思考问题: 为什么下载资源包要用npm install? 请你谈一下 npm run dev: 启动 vue-cli 开发环境的 npm命令(3.0以下?? ? 脚手架2启动方式) npm run serv...

拎着蜗牛跑的壳儿 3086

Vue打包并发布项目

一、 打包vue项目步骤: 1、对当前vue项目进行打包的命令如下: npm run build 2打包完成,会输出Build complete并且在vue项目中会生成一个名字为dist的打包文件。如下图: 二、 使用静态服务器工具包发布打包vue项目 1、首先安装全局的serve,如下图: 命令如下:serve + 打包文件名 serve dist 效果图如下: 3、使用浏览器访问上图中输出的地址,效果图如下: 三、 使用动态 web 服务器(tomcat)发布打包vue项目

qq_43842093的博客 1万+

打包vue项目的方法

部署时,前端vue项目打包方法

weixin_57529171的博客 8996

vue使用npm run build命令打包项目

当我们使用vue-cli脚手架完成一个项目的时候,下一步肯定会想要怎么把这个项目放到互联网上或者本地直接打开呢,我们在本地调试的时候只要命令行执行就可以把这个项目跑起来,但是现在我们要把他放到服务器上的话用命令就可以啦。转存失败重新上传取消。

m0_74387152的博客 3514

使用maven同时打包springboot+vue项目

工程项目结构 父工程POM文件 <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <confi..

s_p_y_s的博客 2274

使用electron将vue项目打包成exe桌面应用

Electron相当于一个浏览器的外壳,可以把现有的vue程序嵌入到壳里面,vue项目中添加 electron 模块,使用 npm run electron:serve 进行启动项目,对 electron 进行配置,打包 exe桌面应用。

初辰的博客 5867

vue-cli 3.0脚手架配置及扩展 (六):npm run serve 和 npm run build 拦截修改

修改文件为:path node_modules/@vue/cli-service/bin/vue-cli-service.js,该文件的修改基于vue-cli的版本为3.0.0-rc.2版本,这是node_modules内部文件,所以在升级vue-cli3.0时会被覆盖掉,所以建议存档,并且修改package.json中的本地开发依赖配置对象devDependencies,修改以‘@vue/’开头...

Franks.T.D 2万+
上一篇: Vue 响应式原理
下一篇: Vue2剥丝抽茧-响应式系统之watch2
web老猴子
博客等级 码龄4年 160粉丝 359原创
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值