大家好!

文本是为了提升开发效率及体验实践诞生的。

项目背景:

  • 脚手架:vue-cli3,具体为 "@vue/cli-service": "^3.4.1"
  • 库:vue2,具体为:"vue": "2.6.12"
  • 备注:没有 typescript , 非 ssr

痛点:随着时间的推移、业务的不断迭代,依赖、功能、代码越来越多,项目本地启动比较慢、开发热更新比较慢。

改进目标:保留原来的 webpack, 支持 vite。并且尽可能的减少改动,减少维护成本。

考虑:

  • vite 生产环境用的是 rollup,rollup 更适合打包库。
  • vite 打包提效并没有提供太多。
  • 保留原来的 webpack 方式,尽可能的保证生产的稳定与安全。

实践

主要是处理三方面:

  • 配置文件需根据 vue.config.js 增加 vite.config.js
  • 入口文件 index.html 支持 vite 的方式
  • 配置 vite 启动命令
  • 可能需要
    • 路由懒加载,vite 需要特殊处理
    • 解决 commonjs 与 esModule 的引入与混用

增加 vite.config.js

在项目根目录创建一个 vite.config.js

安装所需依赖

npm i vite vite-plugin-vue2 -D

根据 vue.config.jsvite.config.js 中增加对应配置

// 若改了该文件逻辑,请对照着改一下 vue.config.js
import path from 'path'
import fs from 'fs'
import { defineConfig } from 'vite'
import config from './config'
import { createVuePlugin } from 'vite-plugin-vue2'
import { injectHtml } from 'vite-plugin-html' const resolve = dir => path.join(__dirname, dir) const alias = {
vue: 'vue/dist/vue.esm.js',
'@': resolve('src'),
} const publicPath = '/'
const mode = 'development' // https://vitejs.dev/config/
export default defineConfig({
base: publicPath,
plugins: [
createVuePlugin(),
],
// 这些是注入项目中的变量,若有 process 开头的,都需要在这儿注入,vite 默认不会注入 process 相关的变量及值
define: {
'process.env.NODE_ENV': JSON.stringify(mode),
'process.env.publicPath': JSON.stringify(publicPath),
},
resolve: {
// 配置别名
alias,
// 导入时想要省略的扩展名列表,必须加入.vue,因为 vite 默认不支持省略 .vue 后缀
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
},
server: {
// 允许跨域
cors: true,
proxy: {
// 可直接拷贝 vue.config.js 中对应的 proxy
}
}
})

sass 相关

配置特殊说明:若项目中有用是 sass 预处理器, 且用到变量了。

需要安装 sass@1.32.13,不能安装比 1.32 更高的版本,跟高版本存在这个问题Sass报错: Using / for division is deprecated

npm i sass@1.32.13  -D
export default defineConfig({
...
css: {
// 给 sass 传递选项
preprocessorOptions: {
scss: {
charset: false, // 最新版sass才支持
additionalData: `@import "src/sass/common/var.scss";`,
}
},
},
...
})

备注:如果生产环境用 vite 打包,采用 sass@1.32.13 将会遇到这个问题vite2打包出现警告,"@charset" must be the first,该如何消除呢?,但是 sass@1.32.13 不支持文档中的配置。所以这可以算是生产环境不用 vite 的一个原因。

还需全局替换 /deep/::v-deep

入口文件 index.html 支持 vite 的方式

由于 vite 的项目要求 index.html 文件在根目录,且有入口文件配置。

所以将 index.html 从 public 目录移到根目录。并加入

<% if (typeof isVite === 'boolean' && isVite) { %>
<script type="module" src="/src/main.js"></script>
<% } %>

这样配置是为了能让 webpack、vite 方式都都支持,共用一个文件

而为了在 index.html 文件注入 isVite 变量,需要安装

npm i vite-plugin-html -D

需要在 vite.config.js 中配置

...
import { injectHtml } from 'vite-plugin-html' export default defineConfig({
...
plugins: [
...
injectHtml({
data: {
title: 'vite-plugin-html-example',
isVite: true
},
}),
],
define: {
'process.env.isVite': JSON.stringify(true)
},
...
})

配置 vite 启动命令

最后在 package.json 中增加脚本

"scripts": {
"vite-start": "vite"
}

路由懒加载,vite 需要特殊处理

vue 实现路由懒加载的方式是这样的

  const Home = import(/* webpackChunkName: "[home]" */ `@/page/home.vue`)

  const routes = [
{
path: `/home`,
name: 'home',
component: Home
},
]

但是 vite 不支持,解决

const modules = import.meta.glob('@/page/*.vue')

const Home = modules['@/page/home.vue']
const modules = import.meta.glob('@/page/*.vue')

// vite 会生成代码
const modules = {
'@/page/home.vue': () => import('@/page/home.vue'),
'@/page/page1.vue': () => import('@/page/page1.vue'),
'@/page/page2.vue': () => import('@/page/page2.vue'),
...
}

参考:vite-Glob 导入

所以可封装一下:

function loadPage (view) {
if (process.env.isVite) {
const modules = import.meta.glob('../pages/*.vue')
return modules[`../pages/${view}.vue`]
} return () => import(/* webpackChunkName: "[request]" */ `@/pages/${view}`)
} // 使用:
const routes = [
{
path: `/home`,
name: 'home',
component: loadPage('home'),
},
{
path: `/404`,
name: '404',
component: loadPage('404'),
},
]

但是 webpack 不支持 import.meta,需要 loader 处理。解决:在本地建一个文件 webpack-import-meta-loader.js。

// 来源:https://github.com/KmjKoishi/webpack-import-meta-loader-fixed
// 是对 @open-wc/webpack-import-meta-loader 这个的修复
// 主要是修复 当 this.rootContext 不存在的判断处理。构建生产环境时不存在 /* eslint-disable */
// @ts-nocheck
const path = require('path');
function toBrowserPath(filePath, _path = path) {
return filePath.replace(new RegExp(_path.sep === '\\' ? '\\\\' : _path.sep, 'g'), '/');
};
const regex = /import\.meta/g; /**
* Webpack loader to rewrite `import.meta` in modules with url data to the source code file location.
*
* @example
* return import.meta;
* // becomes: return ({ url: `${window.location.protocol}//${window.location.host}/relative/path/to/file.js` });
*
* return import.meta.url;
* // becomes: return ({ url: `${window.location.protocol}//${window.location.host}/relative/path/to/file.js` }).url;
*/
module.exports = function (source) {
const path = require('path'); const relativePath = this.context.substring(
this.context.indexOf(this.rootContext) + (this.rootContext && this.rootContext.length >= 0 ? (this.rootContext.length + 1) : 0),
this.resource.lastIndexOf(path.sep) + 1,
); const browserPath = toBrowserPath(relativePath); const fileName = this.resource.substring(this.resource.lastIndexOf(path.sep) + 1); let found = false;
let rewrittenSource = source.replace(regex, () => {
found = true;
return `({ url: getAbsoluteUrl('${browserPath}/${fileName}') })`;
}); if (found) {
return `
function getAbsoluteUrl(relativeUrl) {
const publicPath = __webpack_public_path__;
let url = '';
if (!publicPath || publicPath.indexOf('://') < 0) {
url += window.location.protocol + '//' + window.location.host;
}
if (publicPath) {
url += publicPath;
} else {
url += '/';
}
return url + relativeUrl;
}
${rewrittenSource}`;
} else {
return source;
}
};

vue.config.js 修改配置:

const resolve = dir => require('path').join(__dirname, dir)

module.exports = {
...
configureWebpack: {
...
module: {
rules: {
...
{
test: /index.js$/,
use: [
resolve('webpack-import-meta-loader'),
'babel-loader'
],
include: [resolve('src/router')]
}
}
}
}
...
}

解决 commonjs 与 esModule 的引入与混用

混用

webpack 方式下,若你的 src 项目源码中存在混用 commonjs 与 esModule。

方案一:不改源码,在 vite.config.js 中加配置,把 commonjs 转换为 esModule

安装 npm i cjs2esmodule -D

在 vite.config.js 中加配置

export default defineConfig({
plugins: [cjs2esmVitePlugin()]
})

如果这个方案,能让你的项目运行正常。否则,可能需要采用方案二。

方案二:把 src 代码中的 commonjs 语法自己手动改为 esModule

引入

如果你的项目在有一个 config.js, 被 vue.config.js 中使用。那么你可能需要处理。

vue.config.js 必须是 commonjs 语法的文件,才能被使用,否则会报错。

vite.config.js 既可以 esModule 语法,也可以 commonjs 语法。默认是 esModule 语法。

若上面混用,你是采用方案二,而且 src 代码中也用了 config.js。那么你只能把 config.js 改成 esModule 的方式。此时 vue.config.js 不支持了,采用的方案是根据 config.js 自动生成一个 config-cjs.js。

目的是减少后期维护成本。

// transformConfig2cjs.js

// 运行项目的时候,会根据config.js自动生成文件:config-cjs.js, 以便让 vue-config.js 能直接使用
const {transformAsync} = require('@babel/core');
const plugin = require('@babel/plugin-transform-modules-commonjs')
const fs = require('fs')
const resolve = dir => require('path').join(__dirname, dir)
async function transfrom () {
const inputPath = resolve('config.js')
const input = fs.readFileSync(inputPath, 'utf-8')
const {code} = await transformAsync(input, {
sourceType: 'module',
plugins: [ plugin ]
}); fs.writeFileSync(resolve('./config-cjs.js'), code);
}
transfrom()

然后在 vue.config.js 原来引入的 config.js 的地方改为 config-cjs.

最后在 package.json 中改变下脚本,每次都重新生成一个最新的配置。

"scripts": {
"transformConfig2cjs": "node transformConfig2cjs.js",
"serve": "npm run transformConfig2cjs && vue-cli-service serve",
"build": "npm run transformConfig2cjs && vue-cli-service build",
}

总结

遇到很多坑,都是语法相关的,最终都 11 解决了!

也尝试过一些其他方案,但是不能用,我的项目没有生效:

支持了之后,开发是真的很高效!

希望这篇文章对有需要的有所帮助。

vue-cli3 vue2 保留 webpack 支持 vite 成功实践的更多相关文章

  1. 使用VUE CLI3.0搭建项目vue2+scss+element简易版

    1.安装Vue CLI 3 //三选一即可cnpm install -g @vue/cli npm install -g @vue/cli yarn global add @vue/cli 注意: 1 ...

  2. 一步步构造自己的vue2.0+webpack环境

    前面vue2.0和webpack都已经有接触了些(vue.js入门,webpack入门之简单例子跑起来),现在开始学习如何构造自己的vue2.0+webpack环境. 1.首先新建一个目录vue-wk ...

  3. cordova打包vue2(webpack)android、ios app

    使用cordova打包vue2(webpack)app for android ios1.vue项目通过vue-cli脚手架建立项目,使用webpack进行打包,下边是一整套命令. #npm 版本最好 ...

  4. Vue2.0+Webpack项目环境构建到发布

    前言:为什么要用webpack搭建项目呢,因为这个工具可以把目前浏览器不全部支持的ES6语法,通过打包工具生成所有浏览器都支持的单个JS文件. 参考: https://blog.csdn.net/u0 ...

  5. vue cli3 项目配置

    [转]https://juejin.im/post/5c63afd56fb9a049b41cf5f4 基于vue-cli3.0快速构建vue项目 本章详细介绍使用vue-cli3.0来搭建项目. 本章 ...

  6. 一篇文章说清 webpack、vite、vue-cli、create-vue 的区别

    webpack.vite.vue-cli.create-vue 这些都是什么?看着有点晕,不要怕,我们一起来分辨一下. 先看这个表格: 脚手架 vue-cli create-vue 构建项目 vite ...

  7. 在 vue cli3 的项目中配置双服务,模拟 ajax 分页请求

    最近安装了下vue cli3版本,与 cli 2 相比,文件少了,以前配置方法也不管用了.demo 中的大量的数据,需要做成 ajax 请求的方式来展示数据,因此,需要启动两个服务,一个用作前端请求, ...

  8. Vue2+VueRouter2+webpack 构建项目实战(二):目录以及文件结构

    通过上一篇博文<Vue2+VueRouter2+webpack 构建项目实战(一):准备工作>,我们已经新建好了一个基于vue+webpack的项目.本篇文章详细介绍下项目的结构. 项目目 ...

  9. Vue CLI3 开启gzip压缩

    gizp压缩是一种http请求优化方式,通过减少文件体积来提高加载速度.html.js.css文件甚至json数据都可以用它压缩,可以减小60%以上的体积. webpack在打包时可以借助 compr ...

随机推荐

  1. [bzoj5416]冒泡排序

    结论:一个序列是好序列当且仅当其不存在长度为3的下降子序列 证明:考虑提示,一个长度为3的下降子序列必然会交换三次, 而这三次带来的收益实际上只有2,因此不合法 同时还可以得到:第i个数,要么是前缀最 ...

  2. redis序列化和反序列化的操作-(以前咋操作我都忘记了)

    //拿到数据,redis如果有则将现在有的传进去,如果没有则获取接口 ExWritPropertyVo ExWritPropertyVo = new ExWritPropertyVo(); ExWri ...

  3. 主动扫描之Nmap

    主动扫描之Nmap 本文参考于李华峰等人的图书<Kali Linux2 网络渗透测试实践指南>2018年第1版 目录 主动扫描之Nmap 基本用法 主机发现 端口发现 扫描目标操作系统 扫 ...

  4. 阿里性能专家全方位对比Jmeter和Locust,到底谁更香?

    近些年,随着互联网行业的不断发展,用户规模也有了爆发性的增长.产品的性能成为影响用户体验的重要因素.因此,性能测试越来越受到大型互联网企业的重视. 在做性能测试时,通常都会借助一些压测工具来模拟大量的 ...

  5. 基于Vue简易封装的快速构建Echarts组件 -- fx67llQuickEcharts

    fx67llQuickEcharts A tool to help you use Echarts quickly! npm 组件说明 这本来是一个测试如何发布Vue组件至npm库的测试项目 做完之后 ...

  6. Go 类型强制转换

    Go 类型强制转换 强制类型的语法格式:var a T = (T)(b),使用括号将类型和要转换的变量或表达式的值括起来 强制转换需要满足如下任一条件:(x是非常量类型的变量,T是要转换的类型) 1. ...

  7. nohup使用

    nohup:不挂断运行 在忽略挂起信号的情况下运行给定的命令,以便在注销后命令可以在后台继续运行. 可以这么理解:不挂断的运行,注意并没有后台运行的功能,就是指,用nohup 运行命令可以是命令永远运 ...

  8. URI和URL的区别(转)

    转载:http://www.cnblogs.com/gaojing/archive/2012/02/04/2413626.html 这两天在写代码的时候,由于涉及到资源的位置,因此,需要在Java B ...

  9. Python函数之传参

    目录 1. 函数传参 1.1 参数的作用 1.2 形参和实参 1.3 位置参数 1.4 关键字参数 1.5 默认实参 1.6 参数总结 2. 可变参数 1. 函数传参 1.1 参数的作用 1.2 形参 ...

  10. 3.Median of Two Sorted Arrays Leetcode

    难度系数:5星 /*************************************************************************** 题目:有两个排好序的数组,要求 ...