基于Vue Cli4搭建Vue3 TSX移动端项目(一)
项目简介
Vue3 发布预览版到现在接近一年时间,在之前自己的试验中,对比React Hooks在不考虑针对性优化的时候,Vue3的底层渲染机制优化更佳。所以是时候开始进行Vue3的一些尝试了。
因为我觉得Vue Cli4已经很成熟了,所以没必要从零开始搭建Webpack,在其基础上进行再次封装效率会更高。所以本文基于Vue Cli4进行Vue3框架的搭建,主要包括:
- 区分多环境配置
- 移动端调试工具eruda
- eslint
- stylelint
- px2vw移动端自适应
- HardSource开发构建加速
- babel-plugin-import 按需引入
- 生产环境静态资源上传CDN
- express启动
- 常用Hooks封装
- Vue3 TSX常见语法的写法
在这个项目中,我建议全部使用Component API进行开发,以便更好的运用到Hooks能力。同时为了改善Vue2中<template></template>中的不能进行代码提示等问题,官方提倡使用vue的template语法进行开发,但是template在灵活性方面不如TSX,所以一些灵活性要求高的情况下仍然可以使用TSX进行开发。日常业务代码开发的时候,仍然推荐大家使用template,具体原因会在第四章中说明。在有了Component API之后Vue的TSX写法并不会像之前Vue2使用JSX时那么尴尬,同时TSX与Vue文件之间的也可以进行无缝衔接,完全可以按照业务形态和自身习惯去选择。
在这里因为防止篇幅太长,所以将拆分为四篇进行讲解。本篇主要讲解:项目创建、区分多环境配置、集成eruda移动端调试工具。
创建项目
首先我们通过Vue Cli4来创建基础项目:
项目创建
vue create yuor-template
然后我们选择Manually select features来自定义我们的需要的一些基础配置
Vue CLI v4.5.4
┌──────────────────────────────────────────┐
│ │
│ New version available 4.5.4 -> 4.5.12 │
│ Run npm i -g @vue/cli to update! │
│ │
└──────────────────────────────────────────┘
? Please pick a preset:
Default ([Vue 2] babel, eslint)
Default (Vue 3 Preview) ([Vue 3] babel, eslint)
❯ Manually select features
选择所需特性
这里我们勾选上vue版本选择、Babel、TypeScript、Router、Vuex、CSS预处理、lint
? Check the features needed for your project:
◉ Choose Vue version
◉ Babel
◉ TypeScript
◯ Progressive Web App (PWA) Support
◉ Router
◉ Vuex
◉ CSS Pre-processors
❯◉ Linter / Formatter
◯ Unit Testing
◯ E2E Testing
选择Vue的版本
因为我们这个项目是基于Vue3,所以这里选择的版本为 Vue3
? Choose a version of Vue.js that you want to start the project with
2.x
❯ 3.x (Preview)
其他选择项
因为这里的选择较多,就不一一的再去浪费篇幅,大概配置如下:
分别设置不使用类组件写法、使用babel做转义、router使用history模式、使用dart-sass样式预处理器、使用ESLint + Standard config、保存时进行lint检测、将各个组件配置放在各个组件的配置文件中
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, TS, Router, Vuex, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 3.x (Preview)
? Use class-style component syntax? No
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass)
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? (y/N)
项目试跑
安装完成后,我们便可以进入新建的项目中试跑下
🎉 Successfully created project vue-plus-example.
👉 Get started with the following commands:
$ cd vue-plus-example
$ npm run serve
区分多环境配置
在上面一步中,我们已经通过Vue Cli4创建了基础的项目框架。一般我们在实际开发时,会有本地开发环境local、开发环境development、测试环境test、预发布环境staging、生产环境production。针对这种不同的下我们都需要一套不同的配置来区分例如CDN上传的Bucket、API地址、跳转其他站点的域名等配置。所以接下来我们就来配置一套多环境配置。
添加环境配置文件
首先我们在根目录中新建config文件夹,之后在config文件夹中添加index.js,local.json,development.json,test.json,staging.json,production.json文件。
这里大概解释下每个文件的作用:
- index.js:获取配置文件的方法类,主要封装了根据当前环境变量读取配置的一系列操作
- local.json:本地开发环境的配置,单独拎出这个文件的好处在于当我们在本地开发时想要去调试各个不同环境的接口之类,只需要从其他环境配置文件中拷贝内容覆盖过来,而避免直接修改其他环境的配置文件,导致发版后出现问题的可能。
- development.json:开发环境的配置
- test.json:测试环境的配置
- staging.json:预发布环境的配置
- production.json:生产环境的配置
编写配置文件
这里的配置文件格式只是我目前自己定义的一套结构,可以根据自己的需要去定义自己的结构,下面代码我以development.json的结构为例,其余环境根据自己项目实际配置改下对应的配置值即可
// development.json
{
// 构建时配置
"buildtime": {
// 本地服务IP及端口配置
"origin_server": {
"ip": "127.0.0.1",
"port": "5000"
},
// cdn配置,这个值需要按自己项目中的CDN配置去填写
// 在后续的静态资源上传CDN中会使用到,如果不做静态资源上传CDN的话,那么这个配置可以省略
"cdn": {
"region": "oss-cn-shenzhen",
"accessKeyId": "xxxxxxxxxxxx",
"accessKeySecret": "xxxxxxxxxx",
"bucket": "frontend", // 专门开了一个bucket做前端静态文件存放
"path": "vue-plus/development/", // 这里我用了项目名(vue-plus)+ 环境变量的格式作为路径
"cdnPath": "https://frontend.oss-cn-shenzhen.aliyuncs.com/vue-plus/development/" // 上传后的CDN路径,用于publicPath用
}
},
// 运行时配置
"runtime": {
// 当前环境标识
"env": "development",
// 公共api地址
"api": "https://dev-api.wynneit.cn",
// 账号相关api地址
"account": "https://dev-account-api.wynneit.cn",
// host这里我定义为项目中跳转到其他站点的配置项
"host": {
// mobile站点的域名
"mobile": "https://dev-mobile.wynneit.cn"
},
// 微信相关的配置
"wechat": {
// 微信的APPID
"appId": "wxf8xxxxxxxxxxxa53a"
}
}
}
编写配置读取方法
因为项目最后是使用Docker和Jenkins部署的,所以我这里使用了一个自定义环境参数front_env,从Jenkins配置时传入。
// index.js
const fs = require("fs");
const path = require("path");
const lodash = require("lodash");
const cfgDir = path.join(__dirname);
const env = process.env.front_env;
function generateCfg() {
// 默认配置路径
const defaultConfigPath = path.join(cfgDir, "development.json");
// 本地配置路径
const localConfigPath = path.join(cfgDir, "local.json");
let localConfig = {};
let envConfig = {};
if (fs.existsSync(localConfigPath)) {
localConfig = require(localConfigPath);
}
// 获取
if (env) {
const envCfgPath = path.join(cfgDir, `${env}.json`);
if (fs.existsSync(envCfgPath)) {
envConfig = require(envCfgPath);
} else {
console.warn(
`\nConfiguration file specified by env var ${env} = ${envCfgPath} does not exist.\n`
);
}
}
// 获取默认配置
const defaultConfig = require(defaultConfigPath);
// 拼接最终配置
const mixedConfig = lodash.merge({}, defaultConfig, localConfig, envConfig);
return mixedConfig;
}
module.exports = generateCfg;
挂载config配置
这里我将环境配置挂载到window对象中,这样我们日常在Chrome DevTool中可以很方便的看到当前的一些环境配置,方便一些问题排查。
-
在
/public/index.html添加config插槽<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" /> <link rel="icon" href="<%= BASE_URL %>favicon.ico" /> <title><%= htmlWebpackPlugin.options.title %></title> <!-- 添加自定义配置 --> <%= htmlWebpackPlugin.options.config %> </head> <body> <noscript> <strong >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong > </noscript> <div id="app"></div> <!-- built files will be auto injected --> </body> </html> -
新建
vue.config.js,配置config插槽的值及设置devServer// 获取环境配置 const cfg = require("./config/index")(); module.exports = { devServer: { host: config.buildtime.origin_server.ip, port: config.buildtime.origin_server.port, }, chainWebpack: (config) => { // HTML模板注入配置 config.plugin("html").tap((args) => { // 环境配置script const configScript = `<!--configArea--><script>window.CUSTOMCONFIG = ${JSON.stringify(cfg.runtime)}</script><!--endOfConfigArea-->`; args[0].config = configScript; return args; }); }, }; -
配置完成后,这时候我们跑一下项目,然后在Chrome DevTool中输入
window.CUSTOMCONFIG就可以看到我们当前环境的配置了。
编写config声明文件
虽然上面我们已经成功将config配置挂载到window.CUSTOMCONFIG上了,但是因为我们项目中使用了TypeScript,为了使用时不报错也为了使用时能有正确的代码提示,所以我们还需要针对window.CUSTOMCONFIG书写一个声明文件。
-
在项目目录
src/typings/global下创建window.d.ts文件。这里我用
typings作为声明文件的统一存放文件夹,global作为全局相关的一些声明文件的存放文件夹。 -
编写
window.d.ts文件interface CustomConfig { // 运行时环境变量 env: "local" | "development" | "test" | "staging" | "production"; // 公共接口地址 api: string; // 账号接口地址 account:string; // 跳转其他平台的域名 host: Record<string, string>; // 微信相关配置 wechat: { appId: string, } } interface Window { CUSTOMCONFIG: CustomConfig; } -
在
main.ts中尝试下引入,这是我们就能够window下已经有了CUSTOMCONFIG的代码提示。(如果使用vscode没有提示的话,重启一下即可)。
Eruda移动端调试工具
因为这个项目定位是在移动端,一般在移动端开发中我会选择在非生产环境下嵌入例如vConsole或者Eruda等工具,方便我们在真机调试时能够查看日志,快速定位问题等。因为Eruda的功能更强大,所以这里我选择嵌入Eruda。
添加Html插槽
依旧是在public/index.html中添加自定义配置项 <%= htmlWebpackPlugin.options.eruda %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
/>
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title><%= htmlWebpackPlugin.options.title %></title>
<!-- 自定义配置 -->
<%= htmlWebpackPlugin.options.config %>
<!-- Eruda配置 -->
<%= htmlWebpackPlugin.options.eruda %>
</head>
<body>
<noscript>
<strong
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
properly without JavaScript enabled. Please enable it to
continue.</strong
>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
添加eruda插槽内容
在vue.config.js文件chainWebpack中添加eruda的配置,这里直接使用了CDN,因为这个包只是作为开发调试工具使用,所有没必要去npm install到项目中。这样也可以避免eruda被打包到项目中徒增项目的包体积。
// 获取环境配置
const cfg = require("./config/index")();
module.exports = {
devServer: {
host: cfg.buildtime.origin_server.ip,
port: cfg.buildtime.origin_server.port,
},
chainWebpack: (config) => {
// HTML模板注入配置
config.plugin("html").tap((args) => {
// 嵌入环境配置script
const configScript = `<!--configArea--><script>window.CUSTOMCONFIG = ${JSON.stringify(config.runtime)}</script><!--endOfConfigArea-->`;
args[0].config = configScript;
// 非本地开发环境及非生产环境时,注入eruda
if (!["local", "production"].includes(cfg.runtime.env)) {
const erudaCDN = "//cdn.bootcdn.net/ajax/libs/eruda/2.4.1/eruda.min.js";
const erudaDomCDN = "//cdn.jsdelivr.net/npm/eruda-dom@2.0.0";
const erudaScript = `<!--erudaArea-->
<script src="${erudaCDN}"></script>
<script src="${erudaDomCDN}"></script>
<script>
eruda.init({
tool: ['console', 'network', 'elements', 'resources', 'snippets', 'sources'],
});
eruda.add(erudaDom);
</script>
<!--endOfRrudaArea-->`;
args[0].eruda = erudaScript;
}
return args;
});
},
};
Eruda测试
因为一般我们在本地开发环境时,有Chrome DevTool并不需要Eruda,所有我上面排除了local环境。这里我们修改一下config/local.json中的runtime.env为test来测试一下eruda是否正常安装。修改完成后重新跑一下项目,我们可以看到右下角已经出现了Eruda的图标。