解读 Vue-cli 项目结构及部分主要文件功能作用
上一篇讲解了如何从零开始一步步搭建Vue项目的初始环境。当第一次看到vue项目的文件结构时,估计都会有一种陌生感,不知道那么多文件都是干嘛的。下面一步步来看一下:
开始之前先补充一点,在上一篇文件中,是使用 vue init webpack vue_demo 创建的项目模板的,vue-cli官方为我们提供了5种模板,其中webpack是最常用的模板,一般我们开发项目都会用webpack进行开发,而vue官方也推荐我们使用webpack。下面看看其他剩下的几种模板:
webpack-simple:一个简单webpack+vue-loader的模板,不包含其他功能。
browserify:一个全面的Browserify+vueify 的模板,功能包括热加载,linting,单元检测。
browserify-simple:一个简单Browserify+vueify的模板,不包含其他功能。
simple:一个最简单的单页应用模板。
以上几种具体有差别在哪里,大家可自行创建试试。
一、Vue项目文件结构
我是以Vue-CLI 4.5版本生成的为准,不同版本可能不一样。以下的一级文件夹目录我已经标红出来了,方便区分。以下内容,编辑的我手软,且看且珍惜。
|-- build // 项目构建(webpack)相关代码
| |-- build.js // 生产环境构建代码
| |-- check-version.js // 检查node、npm等版本
| |-- logo.png // 不用说了
| |-- utils.js // 构建工具相关
| |-- vue-loader.conf.js // webpack loader配置
| |-- webpack.base.conf.js // webpack基础配置
| |-- webpack.dev.conf.js // webpack开发环境配置,构建开发本地服务器
| |-- webpack.prod.conf.js // webpack生产环境配置
|-- config // 项目开发环境配置
| |-- dev.env.js // 开发环境变量
| |-- index.js // 项目一些配置变量
| |-- prod.env.js // 生产环境变量
| |-- test.env.js // 测试环境变量
|-- node_modules // 包括各种各样的资源包,类似.net code中package一样,实在太多就不展开说了,有兴趣的可自行查询
|-- src // 源码目录
| |-- assets // 资源文件夹,一般放些图片之类
| |-- components // vue公共组件
| |-- router // vue的路由管理
| | |-- index.js // 路由管理文件
| |-- App.vue // 页面入口文件
| |-- main.js // 程序入口文件,加载各种公共组件
|-- static // 静态文件,比如css文件,json数据等
| |-- .gitkeep // git默认是不允许提交一个空的目录到版本库上的, 可以在空的文件夹里面建立一个.gitkeep文件,然后提交去即可。其实在git中 .gitkeep 就是一个占位符
|-- test// ES6语法编译配置
| |-- e2e//E2E(End To End)即端对端测试,属于黑盒测试,通过编写测试用例,自动化模拟用户操作,确保组件间通信正常,程序流数据传递如预期。
| |-- unit // 单元测试
|-- .babelrc // ES6语法编译配置
|-- .editorconfig // 定义代码格式
|-- .eslintignore// 忽略项目中某个文件夹的提交规范
|-- .eslintrc.js// ESlint的语法检测规则定义
|-- .gitignore // git上传需要忽略的文件格式
|-- .postcsssrc // postcss配置文件,PostCSS是一个用javascript转换CSS的工具,有了它可放心使用CSS未来的语法等
|-- README.md // 项目说明
|-- index.html // 入口页面
|-- package-lock.json // 锁定安装时的包的版本号,并且需要上传到git,以保证其他人在npm install时大家的依赖能保证一致。
|-- package.json // 项目基本信息,包依赖信息等
以上就是vue-cli生成的项目文件结构,一开始看起来感觉挺复杂,通过这样一梳理,感觉就简单多了。在实际的开发过程中,用到最多是src目录,现有结构肯定是不满足,需要自已增加
二、部分文件详细说明
1、包管理文件 packagejson
packagejson文件是项目的配置文件,定义了项目的基本信息以及项目的相关包依赖,npm运行命令等,位于项目根目录下,这也是非常重要的文件。
包括程序的版本定义,作者信息等描述,还有项目依赖版本等,以及相关的运行测试等指令。
在项目中其实还有文件package-lock.json是比较容易被忽视的。引用知乎上一个回答如下:
根据官方文档,这个package-lock.json 是在 `npm install`时候生成一份文件,用以记录当前状态下实际安装的各个npm package的具体来源和版本号。
它有什么用呢?因为npm是一个用于管理package之间依赖关系的管理器,它允许开发者在pacakge.json中间标出自己项目对npm各库包的依赖。
你可以选择以如下方式来标明自己所需要库包的版本,这里举个例子:
"dependencies": {
"@types/node": "^8.0.33",
},这里面的 向上标号^是定义了向后(新)兼容依赖,指如果 types/node的版本是超过8.0.33,并在大版本号(8)上相同,就允许下载最新版本的 types/node库包,
例如实际上可能运行npm install时候下载的具体版本是8.0.35。波浪号大多数情况这种向新兼容依赖下载最新库包的时候都没有问题,可是因为npm是开源世界,
各库包的版本语义可能并不相同,有的库包开发者并不遵守严格这一原则:相同大版本号的同一个库包,其接口符合兼容要求。这时候用户就很头疼了:在完全相同的一个nodejs的代码库,
在不同时间或者不同npm下载源之下,下到的各依赖库包版本可能有所不同,因此其依赖库包行为特征也不同有时候甚至完全不兼容。
因此npm最新的版本就开始提供自动生成package-lock.json功能,为的是让开发者知道只要你保存了源文件,到一个新的机器上、或者新的下载源,
只要按照这个package-lock.json所标示的具体版本下载依赖库包,就能确保所有库包与你上次安装的完全一样。
2、打包基础文件 webpack.base.conf.js
基础的 webpack 配置文件主要根据模式定义了入口出口,以及处理 vue, babel 等的各种模块,是最为基础的部分。其他模式的配置文件以此为基础通过 webpack-merge 合并。
生产模式配置文件 webpack.prod.conf.js
1 var path = require('path') 2 var utils = require('./utils') 3 var config = require('../config') 4 var vueLoaderConfig = require('./vue-loader.conf') 5 // 获取根目录 6 function resolve(dir) { 7 return path.join(__dirname, '..', dir) 8 } 9 module.exports = { 10 // 定义入口文件 11 entry: { 12 app: './src/main.js' 13 }, 14 output: { 15 // 输出路径 16 path: config.build.assetsRoot, 17 // 输出文件名称(name为entry中定义的key值) 18 filename: '[name].js', 19 // 静态资源路径(判断目前所处的环境) 20 // 在开发环境下,路径为根目录 21 // 在生产环境下,路径为根目录下的static文件夹 22 publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath 23 }, 24 resolve: { 25 // 自动解析拓展,可以在引用文件的时候不用写后缀 26 extensions: ['.js', '.vue', '.json'], 27 // 配置别名,避免在结构嵌套过深的情况下出现../../../../xxx这种写法 28 alias: { 29 'vue$': 'vue/dist/vue.esm.js', 30 '@': resolve('src') 31 } 32 }, 33 module: { 34 // 配置不同模块处理规则 35 rules: [{ 36 test: /\.vue$/, 37 loader: 'vue-loader', 38 options: vueLoaderConfig 39 }, { 40 test: /\.js$/, 41 loader: 'babel-loader', 42 include: [resolve('src'), resolve('test')] 43 }, { 44 // 对于图片资源,当文件体积小于10kb时,将其生成为base64,直接插入html中 45 // 当大于10kb时,将图片名称进行按照命名规则进行更改 46 test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 47 loader: 'url-loader', 48 options: { 49 limit: 10000, 50 name: utils.assetsPath('img/[name].[hash:7].[ext]') 51 } 52 }, { 53 // 字体资源打包规则,与图片资源相同 54 test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 55 loader: 'url-loader', 56 options: { 57 limit: 10000, 58 name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 59 } 60 }] 61 } 62 }
3、编译入口 build.js
bulid.js是webpack的打包文件,通过配置package.json中的script来执行脚本。命令号npm run build即运行node build/build.js
1 //bulid.js是webpack的打包文件,通过配置package.json中的script来执行脚本。命令号npm run build即运行node build/build.js 2 'use strict' 3 //检查node+npm的版本,引用./check-versions.js文件 4 require('./check-versions')() 5 6 //process.env是一个包含用户环境信息的对象 NODE_ENV是用户自定义的变量,用来判断是开发环境还是生产环境 7 process.env.NODE_ENV = 'production' 8 9 //导入ora模块 实现loading效果 10 const ora = require('ora') 11 //导入rimraf模块 以包的形式包装rm -rf命令,用来删除文件和文件夹的,不管文件是否为空 12 const rm = require('rimraf') 13 //导入node的path模块 14 const path = require('path') 15 //导入chalk模块 用来改变文字颜色 16 const chalk = require('chalk') 17 //导入webpack模块 18 const webpack = require('webpack') 19 //导入config/index.js 20 const config = require('../config') 21 //导入webpack.prod.conf 22 const webpackConfig = require('./webpack.prod.conf') 23 //实现loading的模块 24 const spinner = ora('building for production...') 25 //开始动画 26 spinner.start() 27 28 //rm方法删除static文件夹 29 //path.join是将路径片段以'\'连接成新的路径,任何一个路径片段有错误就会报错 30 rm(path.join(config.build.assetsRoot,config.build.assetsSubDirectory),err => { 31 if(err) throw err 32 //构建webpack 33 webpack(webpackConfig,(err,stats) => { 34 //停止动画 35 spinner.stop() 36 if(err) throw err 37 //process.stdout.write是标准输出,相当于console.log 38 process.stdout.write(stats.toString({ 39 //增加控制卡颜色开关,即显示不同颜色的字体 40 colors:true, 41 //是否增加内置模块信息 42 modules:false, 43 children:false, 44 //允许较少的输出 45 chunks:false, 46 //不将内置模块的信息加到包信息 47 chunkModules:false 48 49 }) + '\n\n') 50 51 if(stats.hasErrors()){ 52 console.log(chalk.red(' Build complete.\n')) 53 //执行失败 54 process.exit(1) 55 } 56 //编译退出 57 console.log(chalk.cyan(' Build failed with errors.\n')) 58 console.log(chalk.yellow( 59 ' Tip:built files are meant to be served over an HTTP server.\n' + 60 ' Opening index.html over file:// won\'t work.\n' 61 )) 62 }) 63 })
4、打包公用方法代码 utils.js
主要用来处理css-loader和vue-style-loader
1 'use strict' 2 // 引入nodejs路径模块 3 const path = require('path') 4 // 引入config目录下的index.js配置文件 5 const config = require('../config') 6 // 引入extract-text-webpack-plugin插件,用来将css提取到单独的css文件中 7 // 详情请看(1) 8 const ExtractTextPlugin = require('extract-text-webpack-plugin') 9 // 引入package.json后边会用到 10 const packageConfig = require('../package.json') 11 // 是配移动端屏幕自适应像素兼容性75px==1rem 12 const px2remLoader = { 13 loader: 'px2rem-loader', 14 options: { 15 remUnit: 75 16 } 17 } 18 // exports其实就是一个对象,用来导出方法的最终还是使用module.exports,此处导出assetsPath 19 exports.assetsPath = function (_path) { 20 // 如果是生产环境assetsSubDirectory就是'static',否则还是'static',哈哈哈 21 const assetsSubDirectory = process.env.NODE_ENV === 'production' 22 ? config.build.assetsSubDirectory 23 : config.dev.assetsSubDirectory 24 // path.join和path.posix.join的区别就是,前者返回的是完整的路径,后者返回的是完整路径的相对根路径 25 // 也就是说path.join的路径是C:a/a/b/xiangmu/b,那么path.posix.join就是b 26 return path.posix.join(assetsSubDirectory, _path) 27 // 所以这个方法的作用就是返回一个干净的相对根路径 28 } 29 30 // 下面是导出cssLoaders的相关配置 31 exports.cssLoaders = function (options) { 32 // options如果没值就是空对象 33 options = options || {} 34 // cssLoader的基本配置 35 const cssLoader = { 36 loader: 'css-loader', 37 options: { 38 // options是用来传递参数给loader的 39 // minimize表示压缩,如果是生产环境就压缩css代码 40 minimize: process.env.NODE_ENV === 'production', 41 // 是否开启cssmap,默认是false 42 sourceMap: options.sourceMap 43 } 44 } 45 // 为css自动生成兼容性前缀 46 const postcssLoader = { 47 loader: 'postcss-loader', 48 options: { 49 sourceMap: options.sourceMap 50 } 51 } 52 53 // generate loader string to be used with extract text plugin 54 function generateLoaders (loader, loaderOptions) { 55 // 将上面的基础cssLoader配置、兼容性前缀配置、自适应配置放在一个数组里面 56 const loaders = options.usePostCSS ? [cssLoader, postcssLoader, px2remLoader] : [cssLoader, px2remLoader] 57 // 如果该函数传递了单独的loader就加到这个loaders数组里面,这个loader可能是less,sass之类的 58 if (loader) { 59 loaders.push({ 60 // 加载对应的loader 61 loader: loader + '-loader', 62 // Object.assign是es6的方法,主要用来合并对象的,浅拷贝 63 options: Object.assign({}, loaderOptions, { 64 sourceMap: options.sourceMap 65 }) 66 }) 67 } 68 69 // Extract CSS when that option is specified 70 // (which is the case during production build) 71 // 注意这个extract是自定义的属性,可以定义在options里面,主要作用就是当配置为true就把文件单独提取, 72 // false表示不单独提取,这个可以在使用的时候单独配置,瞬间觉得vue作者好牛逼 73 if (options.extract) { 74 return ExtractTextPlugin.extract({ 75 use: loaders, 76 fallback: 'vue-style-loader' 77 }) 78 } else { 79 return ['vue-style-loader'].concat(loaders) 80 } 81 // 上面这段代码就是用来返回最终读取和导入loader,来处理对应类型的文件 82 } 83 84 // https://vue-loader.vuejs.org/en/configurations/extract-css.html 85 return { 86 css: generateLoaders(), // css对应 vue-style-loader 和 css-loader 87 postcss: generateLoaders(), // postcss对应 vue-style-loader 和 css-loader 88 less: generateLoaders('less'), // less对应 vue-style-loader 和 less-loader 89 sass: generateLoaders('sass', { indentedSyntax: true }), // sass对应 vue-style-loader 和 sass-loader 90 scss: generateLoaders('sass'), // scss对应 vue-style-loader 和 sass-loader 91 stylus: generateLoaders('stylus'), // stylus对应 vue-style-loader 和 stylus-loader 92 styl: generateLoaders('stylus') // styl对应 vue-style-loader 和 styl-loader 93 } 94 } 95 96 // Generate loaders for standalone style files (outside of .vue) 97 // 下面这个主要处理import这种方式导入的文件类型的打包,上面的exports.cssLoaders是为这一步服务的 98 exports.styleLoaders = function (options) { 99 const output = [] 100 // 下面就是生成的各种css文件的loader对象 101 const loaders = exports.cssLoaders(options) 102 for (const extension in loaders) { 103 // 把每一种文件的laoder都提取出来 104 const loader = loaders[extension] 105 output.push({ 106 // 把最终的结果都push到output数组中,大事搞定 107 test: new RegExp('\\.' + extension + '$'), 108 use: loader 109 }) 110 } 111 return output 112 }
5、babel配置文件.babelrc
Babel 是广为使用的 ES6 转码器,可以将 ES6 代码转化为 ES5 代码。使用 Babel 第一步就是配置此文件,放在项目根目录,此文件用于配置转码规则和插件
1 { //设定转码规则 2 "presets": [ 3 ["env", { 4 "modules": false, 5 //对BABEL_ENV或者NODE_ENV指定的不同的环境变量,进行不同的编译操作 6 "targets": { 7 "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 8 } 9 }], 10 "stage-2" 11 ], 12 //转码用的插件 13 "plugins": ["transform-vue-jsx", "transform-runtime"] 14 }
6、编码规范.editorconfig
EditorConfig和Prettier一样,都是用来配置格式化你的代码的,这个格式化代码,要和你lint配置相符!否则会出现你格式化代码以后,却不能通过你的代码校验工具的检验
EditorConfig 文件中的设置用于在基本代码库中维持一致的编码风格和设置,例如缩进样式、选项卡宽度、行尾字符以及编码等,而无需考虑使用的编辑器vscode使用editorconfig插件以及.editorconfig配置文件说明详解
或 IDE
1 root = true 2 3 [*] // 对所有文件应用下面的规则 4 charset = utf-8 // 编码规则用utf-8 5 indent_style = space // 缩进用空格 6 indent_size = 2 // 缩进数量为2个空格 7 end_of_line = lf // 换行符格式 8 insert_final_newline = true // 是否在文件的最后插入一个空行 9 trim_trailing_whitespace = true // 是否删除行尾的空格