vue 创建项目之后,我们需要做什么?
Posted 在厕所喝茶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue 创建项目之后,我们需要做什么?相关的知识,希望对你有一定的参考价值。
vue 创建项目之后,我们需要做什么?
前言
现在我们做项目基本都是使用脚手架来初始化项目,不得不说脚手架减少了我们很多工作量。但是我们还需要根据项目的特点,对配置文件进行修改,从而使得我们的开发更加方便快速,甚至还可以做一些性能上的优化。本文就总结一下我使用 vue 脚手架创项目之后所做的一些修改。
创建项目
首先看一下我使用 vue 脚手架创建项目时所选的配置。采用的是typescript
+vue2
+class-component
+scss
开发模式。
雪碧图(精灵图)
在项目中,我们会用到很多小图片。这个时候我们需要借助webpack-spritesmith
这个插件把这些小图片合并成一张大的精灵图。这样做的好处如下:
- 减少小图片的请求次数,这也就意味着网络请求的次数减少了,我们只需要请求一张大的图片回来即可。从而达到了性能上的优化。
- 使用过字体图标的同学应该知道,我们只需要填写对应的类名即可。现在使用雪碧图也是如此,只需要填写对应的类名即可,不需要写
background-image
这些东西,因为webpack-spritesmith
这个插件已经帮我们做好了。而且只要小图片的命名符合规范,对应的类名就可以自动实现hover
效果。这样就可以加快我们的开发效率了。
配置步骤如下:
- 安装
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.png
和star_hover.png
的 png 图片,star_hover.png
图片是鼠标悬浮在star.png
图片上面所需要展现出来的图片。我们只需要icon-${文件名}
这样使用即可
<span class="icon-star"></span>
现在,上面的 span 元素就会显示出一个对应的小图片了,并且鼠标悬浮上去还会有 hover 图片的效果了
注意:如果不存在${文件名}_hover.png
格式名称的图片,鼠标悬浮上去的时候是没有 hover 效果的
全局注入 scss 变量
在项目中我们可能会使用一些 scss 变量,比如主题色,字体大小,背景色等变量,这些变量在很多地方都用到了。为了减少在文件中的引入次数,我们借助style-resources-loader
和vue-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"),
],
},
},
};
注意:这里我强烈建议大家把一些主题色,标题颜色,边框颜色,字号大小等抽取出来放在一个文件中,这样可以方便后期管理。如果你想要做主题变换,这就相当的方便了。同时还有一些背景图,我建议大家也把这些背景图统一放在这里管理
打包分离公共库
我们开发项目的时候,一些库的版本号基本上是不会变的,比如vue
、vuex
、vue-router
、element-ui
等等。这些库我们可以单独进行打包,然后通过webpack.DllPlugin
、webpack.DllReferencePlugin
和add-asset-html-webpack-plugin
引入打包后的文件。这样做的好处是:1、提高构建打包的速度,每次打包的时候可以跳过对这些公共库的打包。2、客户端缓存,因为这些公共库的版本号基本不会变化的,所以我们不需要在打包的时候给这些文件添加 hash 值。这样用户在下载了一次公共库文件之后,后续我们在进行更新,客户端还是使用的是缓存的文件。从而提高用户的体验。
- webpack.dll.config.js 配置
DLLPlugin
是webpack
内置的插件,不需要安装。新建一个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
形式的,其中value
和key
必须是字符串。如果你想传入一个对象,就需要使用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_ENV
、BASE_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 地址:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<!-- 引入 cdn 地址 -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.5.10/vue.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.0.1/vue-router.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.18.0/axios.min.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
图片压缩
图片压缩会影响图片的质量的。如果对图片质量有要求的不建议使用。如果图片数量比较多,并且图片体积比较大,建议把图片进行压缩。这个需要安装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 的相关东西集成进来了。我们要考虑的是代码提交的时候怎么去检查代码是否符合规范,以及在提交的时候自动格式化代码,保持提交的代码统一。还有就是规范提交代码的信息。
关于这部分,我们可以借助husky
,lint-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"
我是如何在 Vue 项目中做代码分割的