vue.js|vue 创建项目之后,我们需要做什么()


vue 创建项目之后,我们需要做什么?

  • 前言
  • 创建项目
  • 雪碧图(精灵图)
  • 全局注入 scss 变量
  • 打包分离公共库
  • 注入全局变量
  • 删除 console.log
  • 代码分割
  • 配置 CDN
  • 图片压缩
  • IgnorePlugin
  • 代码规范
  • 关于CICD(自动部署)
  • 总结

前言 现在我们做项目基本都是使用脚手架来初始化项目,不得不说脚手架减少了我们很多工作量。但是我们还需要根据项目的特点,对配置文件进行修改,从而使得我们的开发更加方便快速,甚至还可以做一些性能上的优化。本文就总结一下我使用 vue 脚手架创项目之后所做的一些修改。
创建项目 首先看一下我使用 vue 脚手架创建项目时所选的配置。采用的是typescript+vue2+class-component+scss开发模式。
vue.js|vue 创建项目之后,我们需要做什么()
文章图片

雪碧图(精灵图) 在项目中,我们会用到很多小图片。这个时候我们需要借助webpack-spritesmith这个插件把这些小图片合并成一张大的精灵图。这样做的好处如下:
  • 减少小图片的请求次数,这也就意味着网络请求的次数减少了,我们只需要请求一张大的图片回来即可。从而达到了性能上的优化。
  • 使用过字体图标的同学应该知道,我们只需要填写对应的类名即可。现在使用雪碧图也是如此,只需要填写对应的类名即可,不需要写background-image这些东西,因为webpack-spritesmith这个插件已经帮我们做好了。而且只要小图片的命名符合规范,对应的类名就可以自动实现hover效果。这样就可以加快我们的开发效率了。
【vue.js|vue 创建项目之后,我们需要做什么()】配置步骤如下:
  • 安装webpack-spritesmith
npm i webpack-spritesmith -D

  • 修改vue.config.js
const SpritesmithPlugin = require("webpack-spritesmith"); const path = require("path"); module.exports = { configureWebpack: (config) => { config.plugins.push( new SpritesmithPlugin({ src: { // 小图片的存放路径 cwd: path.resolve(__dirname, "./src/assets/images/icons"), //后缀名为.png的图片将会被合并成一张大的雪碧图 glob: "*.png", }, target: { // 生成出来的雪碧图的存放路径 image: path.resolve(__dirname, "./src/assets/images/sprites.png"), // 生成出来雪碧图样式存放路径 css: [path.resolve(__dirname, "./src/assets/css/sprites.scss")], }, apiOptions: { // 生成出来的雪碧图相对于生成出来雪碧图样式的路径 cssImageRef: "../images/sprites.png", }, spritesmithOptions: { // 排版方向,自上而下排版 algorithm: "top-down", }, }) ); }, };

  • 重写雪碧图样式
首先新建一个.scss样式文件,然后引入到入口文件中。我们将在这个新建的.scss样式文件中重写雪碧图的样式。
// 引入生成出来的雪碧图样式文件 @import "./sprites.scss"; @mixin spritesRewrite($sprites) { @each $sprite in $sprites { //小图片的名称 $sprite-name: nth($sprite, 10); // hover效果的小图片名称 $sprite-hover-name: #{$sprite-name}_hover; .icon-#{$sprite-name} { display: inline-block; @include sprite($sprite); //判断是否存在对应的hover小图片 @if variable-exists($sprite-hover-name) { &:hover { @extend .icon-#{$sprite-hover-name}; } } } } }@include spritesRewrite($spritesheet-sprites);

  • 使用
首先要先在上面配置文件中的icons目录下发小图片。比如现在目录下有文件名为star.pngstar_hover.png的 png 图片,star_hover.png图片是鼠标悬浮在star.png图片上面所需要展现出来的图片。我们只需要icon-${文件名}这样使用即可
class="icon-star">

现在,上面的 span 元素就会显示出一个对应的小图片了,并且鼠标悬浮上去还会有 hover 图片的效果了
注意:如果不存在${文件名}_hover.png格式名称的图片,鼠标悬浮上去的时候是没有 hover 效果的
全局注入 scss 变量 在项目中我们可能会使用一些 scss 变量,比如主题色,字体大小,背景色等变量,这些变量在很多地方都用到了。为了减少在文件中的引入次数,我们借助style-resources-loadervue-cli-plugin-style-resources-loader将这些 scss 变量注册为全局变量。这样就不用在使用这些 scss 变量的时候去引入 scss 变量文件,大大地提高了我们的开发效率。
  • 安装
npm i style-resources-loader vue-cli-plugin-style-resources-loader -D

  • 修改vue.config.js
const path = require("path"); module.exports = { pluginOptions: { /** * 全局 scss 变量配置 * @param {string | string[]} patterns 你想要全局注册的 scss 变量 */ "style-resources-loader": { preProcessor: "scss", patterns: [ path.resolve(__dirname, "src/assets/styles/variables.scss"), path.resolve(__dirname, "src/assets/styles/mixins/*.scss"), ], }, }, };

注意:这里我强烈建议大家把一些主题色,标题颜色,边框颜色,字号大小等抽取出来放在一个文件中,这样可以方便后期管理。如果你想要做主题变换,这就相当的方便了。同时还有一些背景图,我建议大家也把这些背景图统一放在这里管理
打包分离公共库 我们开发项目的时候,一些库的版本号基本上是不会变的,比如vuevuexvue-routerelement-ui等等。这些库我们可以单独进行打包,然后通过webpack.DllPluginwebpack.DllReferencePluginadd-asset-html-webpack-plugin引入打包后的文件。这样做的好处是:1、提高构建打包的速度,每次打包的时候可以跳过对这些公共库的打包。2、客户端缓存,因为这些公共库的版本号基本不会变化的,所以我们不需要在打包的时候给这些文件添加 hash 值。这样用户在下载了一次公共库文件之后,后续我们在进行更新,客户端还是使用的是缓存的文件。从而提高用户的体验。
  • webpack.dll.config.js 配置
DLLPluginwebpack内置的插件,不需要安装。新建一个webpack.dll.config.js文件:
const path = require("path"); const webpack = require("webpack"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); module.exports = { mode: "production", entry: { // 需要打包的公共库 vendor: ["vue", "vuex", "vue-router", "axios"], }, output: { // 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称 filename: "[name].dll.js", path: path.resolve(__dirname, "lib/dll"), // library必须和后面dllplugin中的name一致 library: "[name]_[hash]", }, plugins: [ new CleanWebpackPlugin(), new webpack.DllPlugin({ // 动态链接库的全局变量名称,需要和 output.library 中保持一致 name: "[name]_[hash]", // 描述动态链接库的 manifest.json 文件输出时的文件名称 path: path.join(__dirname, "lib/dll", "[name]-manifest.json"), }), ], };

然后我们在命令行中输入
webpack --config webpack.dll.config.js

  • 修改 vue.config.js
const webpack = require("webpack"); const AddAssetHtmlPlugin = require("add-asset-html-webpack-plugin"); const path = require("path"); module.exports = { configureWebpack(config) { const plugins = []; plugins.push( // 告诉webpack使用了哪些第三方库代码 new webpack.DllReferencePlugin({ // manifest.json 文件路径 manifest: require(path.resolve( __dirname, "lib/dll/vendor-manifest.json" )), }) ); plugins.push( // 自动给html文件添加静态资源文件 new AddAssetHtmlPlugin({ // 公共库打包出来的文件路径 filepath: path.resolve(__dirname, "lib/dll/vendor.dll.js"), // 引用路径 publicPath: "./statics/dll", // 最终输出的目录 outputPath: "./statics/dll", }) ); config.plugins = [...config.plugins, ...plugins]; }, };

注入全局变量 有时候我们可能会根据一些环境变量去做不同的处理。这里我们有 2 中方式去注入全局变量,一种是借助webpack.DefinePlugin,另外一种是借助脚手架自带的。通常我们会往全局中注入版本号,打包时间,打包人等信息,然后把这些信息挂载到 window 上面。项目放到正式环境之后可以在浏览器控制台中输入变量名称,查看版本号,打包时间等信息,方便检查是否已经进行更新了。
  • 使用webpack.DefinePlugin
webpack.DefinePlugin 传入的全局变量是key-value形式的,其中valuekey必须是字符串。如果你想传入一个对象,就需要使用JSON.stringify将它转化为 json 字符串。
vue.config.js修改如下:
const webpack = require("webpack"); module.exports = { configureWebpack(config) { const plugins = []; const VERSION = require("./package.json").version.replace(/\./g, "") + getFormatDate(new Date()); plugins.push( new webpack.DefinePlugin({ PROJECT_MESSAGE: JSON.stringify({ version: VERSION, author: "xxx" }), }) ); }, };

在客户端代码中访问
console.log(PROJECT_MESSAGE);

  • 使用脚手架自带的
在项目根目录下放置下列文件来指定环境
.env# 在所有的环境中被载入 .env.local# 在所有的环境中被载入,但会被 git 忽略 .env.[mode]# 只在指定的模式中被载入 .env.[mode].local# 只在指定的模式中被载入,但会被 git 忽略

其中mode有:development、test 、production。
文件内容只能包含键=值,如下:
FOO=bar VUE_APP_NOT_SECRET_CODE=some_value

注意:只有NODE_ENVBASE_URL和以VUE_APP_开头的变量才能被注入到客户端代码中。
上述的方式只能是一些写死的变量,对于一些动态变量,比如版本信息,我们可以在vue.config.js中计算环境变量,但是还是需要以VUE_APP_开头。
const pack = require("./package.json"); process.env.VUE_APP_MESSAGE = JSON.stringify({ version: pack.version, name: "xxx", }); module.exports = { // config };

在客户端代码中访问
console.log(process.env.VUE_APP_SECRET);

删除 console.log 在开发的时候,有些开发人员喜欢使用console.log进行调试,但是到了部署到线上的时候,需要删除这些代码。原因如下:1、减少项目体积。别看console.log这个代码并不多,当项目到了一定规模的时候,还是会占用不少的体积的。2、防止信息泄露。有时候我们会打印出一些用户信息,如果不及时删除,可能会泄露了用户的信息。所以我们可以在vue.config.js中配置如下代码。
module.exports = { configureWebpack: (config) => { // 生产环境下生效 if (process.env.NODE_ENV === "production") { // 配置删除 console.log config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true; } }, };

代码分割 对于一个 vue 项目,在打包的时候,会有一个叫chunk-vendors的 js 文件,这个文件包含了一些业务代码和第三方库的代码,所以体积一般比较大,浏览器加载的时长也比较长。我们可以把这部分代码进行分割,分成多个文件。这样浏览器在加载时会并行加载,对于不变的代码,会直接从缓存中读取,提升了文件的访问速度。在vue.config.js中配置如下代码:
module.exports = { configureWebpack: (config) => { config.when( process.env.NODE_ENV === "production", // 配置生产环境生效 (config) => { config.optimization.splitChunks({ chunks: "all", // 将对什么类型的代码进行分割,三种值:all: 全部 | async: 异步,按需加载的代码 | initial: 入口代码块 cacheGroups: { // 缓存组 // 定义 libs 缓存组,分割从 node_modules 中引入的代码 libs: { name: "chunk-libs", // 分割成的文件名 test: /[\\/]node_modules[\\/]/, // 匹配 node_modules 中模块 priority: 10, // 优先级,当模块同时命中多个缓存组的规则时,分配到优先级高的缓存组 chunks: "initial", // 这里覆盖上面的 chunks: 'all',仅打包最初依赖的第三方库 }, // 项目使用 iview 组件开发的,定义 iviewUI 缓存组,用于分割 iview 代码 iviewUI: { name: "chunk-iviewUI", priority: 20, // 优先级 20,命中 iview 代码时,优先分割到此组里 test: /[\\/]node_modules[\\/]_?iview(.*)/, // 匹配 iview 代码 }, }, }); } ); }, };

注意:在我们写 vue 路由的时候,通常会使用动态引入模块的方式来加载路由组件,方式如下:
import VueRouter from "vue-router"; const router = new VueRouter({ routes: [ { path: "/home", name: "home", component: () => import(/* webpackChunkName: "home" */ "../views/home/index.vue"), }, ], });

我们引入模块的时候最好可以给它来一个webpackChunkName表示打包出来的文件名,相同功能或者模块的使用同一个webpackChunkName表示。因为每个文件打包出来的体积可能只有几 kb,这样子会增加请求的次数,所以我们需要把相同功能或者模块的代码取一个相同的webpackChunkName,这样子在打包的时候才能把这些代码放置到同一个文件中。反正打包出来的文件不能太大也不能太小要适中,太小会增加请求次数,太大影响加载的时长。
配置 CDN 这种方式很少见,因为考虑到 cdn 可能会崩掉(反正我没见过崩掉),或者其他原因。不过这也是一种优化项目的方案,毕竟在自家服务器带宽或者流量有限制的时候,使用 cdn 能大大的减少后台的压力。
注意:如果采用了上面 打包分离公共库 的方案,就不要使用这种方案了。
vue.config.js中配置如下代码:
module.exports = { configureWebpack: (config) => { if (process.env.NODE_ENV === "production") { // 配置 cdn,这里将 vue,vue-router 和 axios 三个包配置成 cdn 引入 // 其中 Vue,VueRouter 等名称是该库暴露在全局中的变量名 config.externals = { vue: "Vue", "vue-router": "VueRouter", axios: "axios", }; } }, };

然后在public/index.html模板文件中引入 cdn 地址:
src="http://img.readke.com/220919/192AQN6-1.jpg"> src="http://img.readke.com/220919/192AR133-2.jpg"> src="http://img.readke.com/220919/192AT938-3.jpg">

图片压缩 图片压缩会影响图片的质量的。如果对图片质量有要求的不建议使用。如果图片数量比较多,并且图片体积比较大,建议把图片进行压缩。这个需要安装image-webpack-loader
vue.config.js中配置如下代码:
module.exports = { chainWebpack: (config) => { // 配置图片压缩 config.module .rule("images") .use("image-webpack-loader") .loader("image-webpack-loader") .options({ bypassOnDebug: true, }) .end(); }, };

IgnorePlugin 有些依赖包,里面一些文件是没有用到的,但是打包的时候也会被一起打包进去,这个时候就可以使用IgnorePlugin来忽略这部分文件的打包。
moment 为例,忽略语言包,在vue.config.js中配置如下代码:
module.exports = { chainWebpack: (config) => { config.plugins.push( new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) // 忽略打包语言包 ); }, };

代码规范 我们在使用 vue 脚手架创建项目的时候就已经把 eslint 的相关东西集成进来了。我们要考虑的是代码提交的时候怎么去检查代码是否符合规范,以及在提交的时候自动格式化代码,保持提交的代码统一。还有就是规范提交代码的信息。
关于这部分,我们可以借助huskylint-staged这些工具。但是要注意的是,我们这里安装的husky版本是4.x的,最新版本已经去到了6.x。但是6.x版本的似乎并不是很好用,而且用法也发生了很大改变。所以我们还是用回4.x的版本
  • 安装依赖
npm i husky@4.2.5 lint-staged commitizen @commitlint/cli @commitlint/config-conventional stylelint stylelint-config-recess-order stylelint-config-recommended-scss stylelint-config-standard stylelint-scss -D

  • 提交前检查代码,并自动格式化代码
    提交代码前先使用prettier对代码进行格式化,这样可以保证提交上去的代码格式统一。然后在使用 eslint对代码进行自动修复,最后在使用eslint检查代码是否符合规范,符合就可以提交,不符合就不能进行提交。在package.json中添加如下代码:
"husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "*.{js,vue,ts}": [ "prettier --write", "vue-cli-service lint --fix", "vue-cli-service lint", "git add" ] },

在项目根目录新建一个.prettierrc 文件,写入一下配置,用于给prettier插件进行格式化的:
{ "singleQuote": true, "trailingComma": "none", "endOfLine": "auto" }

  • 关于样式
vuecli 生成的项目只有eslint,并没有样式文件检查的相关东西。我们需要自行进行配置。在项目根目录新建一个.stylelintrc 文件,写入以下配置,用于给stylelint插件进行代码校验和修复的:
{ "extends": [ "stylelint-config-standard", "stylelint-config-recommended-scss", "stylelint-config-recess-order" ], "plugins": [ "stylelint-scss" ], "rules": { "font-family-no-missing-generic-family-keyword": null, "indentation": 2, "no-descending-specificity": null, "selector-pseudo-class-no-unknown": null, "selector-pseudo-element-no-unknown": [ true, { "ignorePseudoElements": ["v-deep"] } ], "property-no-unknown": null, "declaration-colon-newline-after": null } }

如果你需要忽略某些样式文件,可以新建一个.stylelintignore文件,写法跟.gitignore文件一样
package.json 中添加如下代码:
"husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "*.{html,vue,css,sass,scss}": [ "stylelint **/*.{html,vue,css,sass,scss} --fix" ] },

  • 检查提交信息规范
    这里参考了angular的提交信息规范。在package.json中添加如下代码:
"scripts": { "commit": "git cz" }, "husky": { "hooks": { "commit-msg": "commitlint -e $HUSKY_GIT_PARAMS" } }, "config": { "commitizen": { "path": "./node_modules/cz-conventional-changelog" } },

然后再项目根目录下新建一个commitlint.config.js文件,写入以下代码
module.exports = { extends: ["@commitlint/config-conventional"], rules: { "type-enum": [ 2, "always", [ "build", // 主要目的是修改项目构建系统(例如 glup,webpack,rollup 的配置等)的提交 "ci", // 主要目的是修改项目继续集成流程(例如 Travis,Jenkins,GitLab CI,Circle等)的提交 "docs", // 文档更新 "examples", // 开发测试 "feat", // 新增功能 "fix", // bug 修复 "perf", // 性能优化 "refactor", // 重构代码(既没有新增功能,也没有修复 bug) "style", // 不影响程序逻辑的代码修改(修改空白字符,补全缺失的分号等) "test", // 新增测试用例或是更新现有测试 "revert", // 回滚某个更早之前的提交 "chore", // 不属于以上类型的其他类型(日常事务) ], ], }, };

最后,提交代码的时候使用npm run commit即可。
关于前端代码工作流这部分的东西,可以参考我的另一篇博客,里面有更详细的说明。
关于CICD(自动部署) 这里说明一下,我们的自动部署是针对测试环境的,并不针对生产环境的,生产环境是前端把包打好发送给后台,由后台统一去更新。
说到CICD有人可能第一时间想到的就是jenkins。但是我们并不会去考虑使用jenkins,原因如下
  • jenkins比较吃硬件,占用内存也比较高,服务器资源有限。而且只是为了部署前端而去搞这个东西就显得有点鸡肋。
  • 打包速度慢。我们测试过jenkins一次打包更新大概需要5-8分钟。jenkins打包经历四部分,拉取代码–安装依赖–打包–部署。当然,慢还是跟硬件有点关系的,这就是上面所说的吃硬件。
  • 钩子函数触发频繁,导致更新频繁。jenkins打包更新是依靠webhook来触发的,一旦有提交就会触发webhook的。而我们提交代码都是比较细化,所以可能会提交很多次,从而多次触发更新。
  • 开发人员需要控制何时去更新。
所以我们的做法就会自己实现一个本地自动化部署脚本,开发人员只需要运行npm run deploy即可打包更新到测试环境。具体打包部署流程如下:拉取代码–安装依赖–打包代码–压缩成giz文件–ssh连接服务器–删除服务器上的旧文件–上传giz文件–解压giz文件–删除giz文件。这样子就很方便我们开发人员进行开发测试和更新。
关于这部分的代码,大家可以参考我的另一篇博客
另外,如果大家对jenkins有兴趣的,可以参考我的这篇博客
总结 上面的东西都是基于我平时工作开发中总结出来的。有的属于项目上的性能优化,比如雪碧图,图片压缩等等。有的属于工程化的,比如代码规范,自动化部署。相信这些东西大家在平常的开发中或多或少都会用到一些的。如果大家还有什么要补充的欢迎下方留言。

    推荐阅读