基于Vue Cli4搭建Vue3 TSX移动端项目(二)
上一篇我们讲解了项目创建、区分多环境配置、集成eruda移动端调试工具。本篇我们来进行移动端自适应、HardSource开发构建加速、babel-plugin-import 按需引入、eslint、stylelint的配置。
移动端自适应
常规方案
我们在做移动端自适应时一般有2种方案:
-
px2rem:css3的rem是基于根元素的字体大小计算的尺寸单位,所以通过改变html的
font-size来实现rem的响应式布局。常规来说我们会通过js来根据屏幕宽度改变根元素font-size的值,例如引入lib-flexible/flexible包,再配置postcss-pxtorem插件来实现。 -
px2vw:px2rem的进阶版,将我们css中的px单位转换成vw,不需要对根元素的
font-size进行动态设置,可以根据屏幕宽度实时变化,缺点就是兼容性方面不如rem来的好。但是px2vw目前的表现中,已经基本满足了大多数设备的使用要求,所以这里我还是会采用px2vw来做为移动端自适应方案。
安装依赖
在终端中执行npm install -D postcss-px-to-viewport安装所需依赖
✗ npm install -D postcss-px-to-viewport
npm WARN postcss-modules@4.0.0 requires a peer of postcss@^8.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN icss-utils@5.1.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-extract-imports@3.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-local-by-default@4.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-scope@3.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-values@4.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
+ postcss-px-to-viewport@1.1.1
added 1 package from 1 contributor in 6.076s
配置PostCss
我们在vue.config.js中引入postcss-px-to-viewport插件,然后再在css.loaderOptions.postcss中新增plugin,将项目中所有的px转换为vw,默认设计图宽度为750px。
这里我还在css.loaderOptions.sass中全局引入了一个样式文件common.scss,存放一些全局样式。
按以下配置,后续开发时只需将设计图宽度配置为750px即可完全按照设计图所示的数值进行开发
// 获取环境配置
const cfg = require("./config/index")();
const Pxtovw = require("postcss-px-to-viewport");
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;
});
},
css: {
loaderOptions: {
// 全局引入common.scss
sass: {
prependData: '@import "~@/assets/styles/common.scss";',
},
// 配置px2vw
postcss: {
plugins: [
new Pxtovw({
unitToConvert: "px", // 需要转换的单位,默认为"px";
viewportWidth: 750, // 设计稿的视口宽度
unitPrecision: 5, // 单位转换后保留的小数位数
propList: ["*"], // 要进行转换的属性列表,*表示匹配所有,!表示不转换
viewportUnit: "vw", // 转换后的视口单位
fontViewportUnit: "vw", // 转换后字体使用的视口单位
selectorBlackList: [], // 不进行转换的css选择器,继续使用原有单位
minPixelValue: 1, // 设置最小的转换数值
mediaQuery: false, // 设置媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
exclude: [/node_modules/], // 忽略某些文件夹下的文件
}),
],
},
},
},
};
效果测试
我们在src/views/Home.vue中对Vue的Logo图添加class="logo",然后再在style中给.logo添加width: 600px。
因为我们上面配置的屏幕宽度为750px。所以这里logo的width: 600px即会换算成80vw。我们再将项目跑起来,可以看到Logo图的宽度已经换算成80vw了。
HardSource开发构建加速
每个项目随着业务的变更都会变得越来越庞大,打包速度也会越来越慢。在之前webpack dll动态链接库的方案可谓是很多人用的经典方案,但是随着webpack的升级,打包性能还是逐步提高了很多,我们也希望减少配置带来的复杂关系,那么最优化最小化配置的,必然是最好的选择。
Hard-Source-Webpack-Plugin是Webpack Dll一个很好的替代者,它可为模块提供中间缓存步骤。第二次构建将明显更快。所以这里我采用了Hard-Source-Webpack-Plugin来作为代替DLL的打包优化方案。
安装依赖
npm install -D hard-source-webpack-plugin
✗ npm install -D hard-source-webpack-plugin
npm WARN postcss-modules@4.0.0 requires a peer of postcss@^8.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN icss-utils@5.1.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-extract-imports@3.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-local-by-default@4.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-scope@3.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-values@4.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
+ hard-source-webpack-plugin@0.13.1
added 17 packages from 4 contributors in 8.192s
配置HardSourceWebpackPlugin
HardSourceWebpackPlugin的配置很简单,我们只需要在configureWebpack.plugins将其配置进去即可。
// 获取环境配置
const cfg = require("./config/index")();
const Pxtovw = require("postcss-px-to-viewport");
const HardSourceWebpackPlugin = require("hard-source-webpack-plugin");
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;
});
},
configureWebpack: {
// 配置HardSourceWebpackPlugin
plugins: [new HardSourceWebpackPlugin()],
},
css: {
loaderOptions: {
// 全局引入common.scss
sass: {
prependData: '@import "~@/assets/styles/common.scss";',
},
// 配置px2vw
postcss: {
plugins: [
new Pxtovw({
unitToConvert: "px", // 需要转换的单位,默认为"px";
viewportWidth: 750, // 设计稿的视口宽度
unitPrecision: 5, // 单位转换后保留的小数位数
propList: ["*"], // 要进行转换的属性列表,*表示匹配所有,!表示不转换
viewportUnit: "vw", // 转换后的视口单位
fontViewportUnit: "vw", // 转换后字体使用的视口单位
selectorBlackList: [], // 不进行转换的css选择器,继续使用原有单位
minPixelValue: 1, // 设置最小的转换数值
mediaQuery: false, // 设置媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
exclude: [/node_modules/], // 忽略某些文件夹下的文件
}),
],
},
},
},
};
效果验证
OK,接下来我们对比一下效果,图一是没有开启HardSource,图二是开启了HardSource的。当然因为现在项目并不大,所以效果并不是很明显,但是当在Webpack-dev-server开发时,热加载的时候还是会有明显的速度加快,比较玄学。
Babel-plugin-import按需引入
ts-import-plugin插件
我个人并不排斥使用第三方成熟的组件库,所以这个项目中我引入了Vant。官方文档上TypeScript推荐使用ts-import-plugins但是我实际使用过程中,发现ts-import-plugins有BUG。
具体复现路径为:
- 先在页面中引入Vant相关的一些组件,例如
<Radio>之类的 npm run serve运行项目,随意修改一下<Radio>组件上的文案Ctrl + C结束运行- 再次
npm run serve运行项目,然后修改<Radio>组件上的文案 - 查看页面展示,会发现此时Vant的样式失效了。非百分百复现,可以重复2、3步进行测试
- 失效后只有删除
node_modules/.cache/ts-loader文件夹,清除ts-loader缓存才能恢复正常
babel-plugin-import插件
虽然说这个问题只影响开发环境,打包后样式正常,但是还是会影响到日常的开发。所以这里我不再使用ts-import-plugin而是改用修改tsconfig.json + babel-plugin-import 来做按需引入。
首先我们修改tsconfig.js,在compilerOptions中修改:
-
target:编译后输出的是什么js版本,这里我们修改为
esnext -
module:指定要使用的模块标准,这里我们也修改为
esnext -
jsx:指定jsx代码用于的开发环境,这里我们修改为
preserve,在preserve模式下生成代码中会保留JSX以供后续的转换操作使用(比如:Babel)
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"jsx": "preserve",
"strict": true,
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
OK,修改完tsconfig.json接下来我们进行babel-plugin-import的安装及配置。
首先进行依赖安装:npm install -D babel-plugin-import
npm install -D babel-plugin-import
npm WARN postcss-modules@4.0.0 requires a peer of postcss@^8.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN icss-utils@5.1.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-extract-imports@3.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-local-by-default@4.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-scope@3.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-values@4.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
+ babel-plugin-import@1.13.3
added 1 package from 1 contributor in 7.169s
依赖安装完成后,我们对babel.config.js添加vant相关配置
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
plugins: [
[
"import",
{
libraryName: "vant",
libraryDirectory: "es",
style: true,
},
"vant",
],
],
};
这里的思路在于首先我们通过tsconfig.json将编译后的代码输出为ES6+的版本,使其支持babel转义,再通过配置babel来达到按需引入的作用。很多同学在按照Vant官方文档的babel配置后发现无法按需引入,就是因为少配置了tsconfig.json文件。
效果验证
首先我们全局安装Vant3.x:npm i vant@next -S
✗ npm i vant@next -S
npm WARN postcss-modules@4.0.0 requires a peer of postcss@^8.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN icss-utils@5.1.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-extract-imports@3.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-local-by-default@4.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-scope@3.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-values@4.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
+ vant@3.0.10
added 6 packages from 2 contributors in 7.807s
在src/main.ts中引入Vant的Button
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import { Button } from "vant";
createApp(App).use(store).use(router).use(Button).mount("#app");
我们进行打包,看下现在的包体积,因为Button中包含了Vant的icon相关样式,所以css文件的增长比较感人
然后在src/main.ts中再多引入Vant的Radio
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import { Button, Radio } from "vant";
createApp(App).use(store).use(router).use(Button).use(Radio).mount("#app");
这时候可以看到打包出来的体积与只打包Button组件时多了一些,证明这里按需引入已经是生效了。
Eslint
在团队开发中,保持代码风格的统一性尤为重要。除了我们日常的代码规范约定,还应该从技术手段上进行一定的风格统一。所以在项目中我们一般都会使用Eslint,很多人不喜欢Eslint主要是因为它影响了我们的日常开发效率,因为Vue Cli4在初始化时,已经帮我们把Eslint相关的包安装好了,所以这里我们主要是配置Eslint保存时自动格式化,使我们在使用Eslint统一代码风格的同时不至于过度影响我们的开发效率。
安装VSCode插件
首先我们给VSCode安装 Eslint及Prettier - Code formatter插件
配置.eslintrc.js
因为每个公司中各自的Lint规则都不同,所以这里我就不给出详细的Lint配置规则。只做一个简单的示范,可以根据自己项目的开发规范去设置属于自身的Lint规则
module.exports = {
// 指定配置文件根目录:表示当前文件为eslint的根配置文件,逐层查找时无需往更上一级的文件目录中进行搜索
root: true,
env: { // 运行环境
node: true
},
extends: [ // 继承规则
'plugin:vue/vue3-essential',
'plugin:vue/vue3-recommended',
'@vue/standard',
'@vue/typescript/recommended',
'./node_modules/@mlz/lint/ts-eslintrc.js', // 公司内部的Lint规则库
],
plugins: [ // 提供插件
'vue'
],
parserOptions: { // eslint解析器配置项
parser: '@typescript-eslint/parser',
ecmaVersion: 2020
},
rules: { // 具体规则
'import/no-default-export': 0,
'no-unused-expressions': 'off',
'no-return-assign': 'off',
'promise/no-nesting': 'off',
'@typescript-eslint/no-unused-expressions': 2,
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/camelcase': 'off',
"@typescript-eslint/interface-name-prefix": 'off',
'vue/singleline-html-element-content-newline': 'off',
"@typescript-eslint/no-use-before-define": "off"
}
}
配置.eslintignore
这里我排除掉了dist、bin、config、src/typings文件夹,这里可以根据自身需要去进行Lint排除。
dist/
bin/
config/
vue.config.js
src/typings/
配置.prettirerrc.js
因为这里使用到了prettirerrc,所以配置.prettirerrc.js使格式化标准与Eslint一致。
module.exports = {
tabWidth: 2, // 一个tab代表几个空格数,默认为4
useTabs: false, // 是否使用tab进行缩进,默认为false,表示用空格进行缩减
singleQuote: true, // 字符串是否使用单引号,默认为false,使用双引号
semi: true, // 行位是否使用分号,默认为true
trailingComma: "all", // 是否使用尾逗号,有三个可选值"<none|es5|all>"
bracketSpacing: true, // 对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
parser: "babylon", // 代码的解析引擎,默认为babylon,与babel相同。
arrowParens: "always",
endOfLine: "lf"
};
配置settings.json
上述配置完成后,我们来对VSCode的Settings.json进行配置,让VSCode能够在我们保存时根据Eslint的规则进行自动格式化。在settings.json中添加如下配置:
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.tslint": true
},
"eslint.validate": [
"javascript",
"typescript",
"javascriptreact",
"typescriptreact",
"html",
"vue"
],
配置完成后,确保右下角Eslint图标正常展示,我们可以尝试下保存文件,看下是否会自动按照Eslint规则进行格式化,如果没生效可以重启一下Vscode。
StyleLint
完成了Eslint的配置,接下来我们继续做StyleLint的配置。
首先我们给Vscode安装StyleLint插件
安装依赖
npm i -D stylelint stylelint-scss stylelint-webpack-plugin
✗ npm i -D stylelint stylelint-scss stylelint-webpack-plugin
npm WARN @vue/eslint-config-typescript@7.0.0 requires a peer of @typescript-eslint/eslint-plugin@^4.4.0 but none is installed. You must install peer dependencies yourself.
npm WARN @vue/eslint-config-typescript@7.0.0 requires a peer of @typescript-eslint/parser@^4.4.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules@4.0.0 requires a peer of postcss@^8.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN icss-utils@5.1.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-extract-imports@3.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-local-by-default@4.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-scope@3.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN postcss-modules-values@4.0.0 requires a peer of postcss@^8.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN tslint-eslint-rules@5.4.0 requires a peer of typescript@^2.2.0 || ^3.0.0 but none is installed. You must install peer dependencies yourself.
+ stylelint-webpack-plugin@2.1.1
+ stylelint@13.12.0
+ stylelint-scss@3.19.0
added 127 packages from 93 contributors and updated 1 package in 12.121s
配置.stylelintrc.json
这里我们已经有了一套成熟的规则,所以具体的规则这里不展开讲述。你也可以直接使用第三方的规则库例如:stylelint-config-standard
{
//"extends": "stylelint-config-standard", // 如果没有自定义规则,则可以使用第三方
"extends": "./node_modules/@mlz/lint/stylelintrc.json", // 公司的Stylelint规则
"rules": {
"indentation": 2,
"unit-case": "lower"
}
}
配置.stylelintignore
# build
/dist/
/build/
# nodemodules
node_modules
配置vue.config.js
在vue.config.js中引入stylelint-webpack-plugin,在configureWebpack.plugins中添加StyleLintWebpackPlugin
// 获取环境配置
const cfg = require("./config/index")();
const Pxtovw = require("postcss-px-to-viewport");
const HardSourceWebpackPlugin = require("hard-source-webpack-plugin");
const StylelintWebpackPlugin = require("stylelint-webpack-plugin");
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;
});
},
configureWebpack: {
plugins: [
// 配置HardSource加快二次构建
new HardSourceWebpackPlugin(),
// 配置styleint
new StylelintWebpackPlugin({
files: ['**/*.{vue,htm,html,css,sss,less,scss}'],
fix: true, // 开启自动修复
cache: false, // 开启缓存
emitErrors: true,
failOnError: false,
}),
],
},
css: {
loaderOptions: {
// 全局引入common.scss
sass: {
prependData: '@import "~@/assets/styles/common.scss";',
},
// 配置px2vw
postcss: {
plugins: [
new Pxtovw({
unitToConvert: "px", // 需要转换的单位,默认为"px";
viewportWidth: 750, // 设计稿的视口宽度
unitPrecision: 5, // 单位转换后保留的小数位数
propList: ["*"], // 要进行转换的属性列表,*表示匹配所有,!表示不转换
viewportUnit: "vw", // 转换后的视口单位
fontViewportUnit: "vw", // 转换后字体使用的视口单位
selectorBlackList: [], // 不进行转换的css选择器,继续使用原有单位
minPixelValue: 1, // 设置最小的转换数值
mediaQuery: false, // 设置媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
exclude: [/node_modules/], // 忽略某些文件夹下的文件
}),
],
},
},
},
};
配置settings.json
配置完成后,我们直接在刚刚settings.json中editor.codeActionsOnSave配置项下添加一条:source.fixAll.stylelint。这样Vscode就会在我们保存时,根据StyleLint的规则进行自动格式化了
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.tslint": true,
"source.fixAll.stylelint": true
},