创建项目
vue cli是一个基于vue.js进行快速开发的完整系统,通常包含三个组件,分别是:
- cli:
@vue/cli
是全局安装的NPM包,提供终端vue命令,比如vue create
、vue serve
、vue ui
等命令。 - cli服务:
@vue/cli-service
是开发环境以来,构建在webpack和webpack-dev-server之上,提供诸如serve
、build
、inspect
等命令。 - cli插件,为vue项目提供可选功能的NPM包,比如
Babel/TypeScript
转译、ESLint
集成、UNIT和E2E测试等。
安装
- 安装 vue-cli3 需要 Node.js 8.9+ 版本,推荐Node.js 8.11.0+版本。
- vue-cli3 的包名由
vue-cli
改为@vue/cli
,如果已安装vue-cli1.x或2.x,可先通过npm uninstall -g vue-cli
卸载。
- 检查Node.js版本
- 卸载Vue-cli2.x
- 重新安装@vue/cli
- 创建应用
- 允许服务
# 查看Node.js版本
$ node -v
v12.14.0
# 卸载vue-cli
$ npm uninstall -g vue-cli
# 安装vue脚手架
$ npm i -g @vue/cli
# 查看vue版本
$ vue --version
2.9.6
# 使用脚手架创建项目
$ vue create chat
$ cd chat
$ npm run serve
浏览器访问 http://127.0.0.1:8080
选择预设
创建Vue项目会提示选取一个preset预设,可选默认包含基本的Babel + ESLint
设置的预设,也可手工选择特性。
Vue CLI v4.1.2
? Please pick a preset:
> default(babel, eslint)
Manually select features
- 默认预设:
default(babel, eslint)
默认设置适合快速创建新项目的原型,无任何辅助功能的NPM包。
- 手工选择特性:
Manually select features
手动配置,使用方向键控制,使用空格键选中,使用a键选择。选项是所需面向生产环境的项目,提供可供选择功能的NPM包。
手动配置提供的NPM包
Vue CLI v4.1.2
? Please pick a preset: Manually select features
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection)
>(*) Babel
( ) TypeScript
( ) Progressive Web App (PWA) Support
( ) Router
( ) Vuex
( ) CSS Pre-processors
(*) Linter / Formatter
( ) Unit Testing
( ) E2E Testing
VUE CLI使用一套基于插件的架构,打开package.jsoon
会发现依赖都是以@vue/cli-plugin-
开头。插件可以修改webpack内部配置,也可以向vue-cli-service
注入命令。
安装插件
例如:手工添加eslint
插件,命令会将@vue/eslint
解析为完整的包名@vue/cli-plugin-eslint
,然后从NPM安装并调用其生成器。若不带@vue
前缀,命令会换做解析一个unscoped包,可基于一个指定的scope使用。
$ vue add @vue/eslint
NPM包 | 描述 |
---|---|
Babel | 转码器,将ES6转换为ES5。 |
TypeScript | JS超集扩展了JS语法,需编译输出为JS。 |
PWA | 渐进式Web应用程序 |
Router | Vue路由 |
Vuex | Vue状态管理模式 |
CSS Pre-processors | CSS预处理器 |
Linter/Formatter | 代码风格检查和格式化 |
Unit Testing | 单元测试 |
E2E Testing | e2e测试 |
babel
Babel是一个JavaScript编译器,用于将ECMAScript2015+版本的代码转换为先后兼容的JavaScript语句。
- 语法转换
- 通过Polyfill在目标环境中添加缺失的特性
- 源码转换
vue-router
vue-router默认采用hash模式,也可选择history模式。vue-router利用浏览器自身的hash模式和history模式的特性来实现前端路由,通过了浏览器提供的接口。
- hash:浏览器URL地址栏中
#
符号,hash不被包括在HTTP请求中,对后端完全没有影响,因此改变hash不会重新加载页面。 - history:利用HTML5 History Interface中新增的
pushState()
和replaceState()
方法,需特定浏览器支持,适用于单页客户端应用。history mode徐后台配置支持。
vue cli 3中采用src/router.js
文件替代了vue cli 2的src/router/index.js
文件
vuex
vuex用于状态管理,vue cli 3中默认使用store.js
代替原vue cli 2种store文件夹中的三个JS文件action、mutations、state以及store的getters用法。
css pre-processors
css预处理器用于解决浏览器兼容并简化css代码等问题
目录结构
- vue-cli3.x相比vue-cli2.x目录简洁了很多,没有了build和config等文件夹。
- vue-cli3.x相比vue-cli2.x所创建目录中已经看不到webpack的配置
- vue-cli3.x中创建vue.config.js文件通过configureWebpack来配置webpack
文件 | 描述 |
---|---|
.browserslistrc | 用于指定项目的目标浏览器范围 |
package.json | 定义项目所需模块及项目信息 |
package-lock.json | 锁定安装时的包版本号 |
tsconfig.json | TypeScript配置文件 |
babel.config.js | Babel配置文件 |
.eslintrc.js | eslint检测规则配置 |
.gitignore | GIT配置文件 |
public | 配置ico和index.html |
src | vue项目文件夹 |
src/assets/ | 用于存放项目静态文件,包括图片、JS、SVG等,生产环境下会被WebPack复制。 |
src/components/ | 存放公用Vue组件页面 |
src/styles | 存放重写reset.css以及字体图标CSS文件 |
src/views | 存放较大模块,比如登录页、注册页、首页等。 |
public
vue cli 3摒弃vue cli2的static文件夹新增了public文件夹,vue cli 2中static文件夹是webpack存放默认静态资源的文件夹,打包时会直接复制一份到dist文件夹中,且不会经过webpack编译。vue cli 3中静态资源有两种处理方式:
- 经过webpack处理:在JS被导入或在
template/css
中通过相对路径被引用的资源会编译并压缩。 - 不经过webpack处理:放置在public文件夹下或通过绝对路径被引用的资源将会直接拷贝一份,且不做任何编译压缩处理。
vue cli 3中public/index.html
模板会被html-webpack-plugin
处理。
src/views
vue cli 3的src文件夹中新增了views文件夹用于存放页面,用于区分components组件。
环境配置.env
项目中通常包含多种模式,常见比如开发模式development
、生产模式production
等,开发中会根据环境变量process.env.NODE_ENV
进行区分。
在根目录下创建.env.production
生产环境配置和.env.development
开发环境配置,配置文件以键值对的方式,配置项必须以VUE_APP_
以前缀。NODE_ENV
和BASE_URL
是两个特殊变量,在代码中始终可用。
$ vim .env.development
BASE_URL = http://127.0.0.1:8080
VUE_APP_API_URL = http://127.0.0.1:4000
VUE_APP_WX_APPID = wx6fe244f7d2197mc1
加载环境配置文件
vue会根据启动命令启动加载对应的环境文件,这是因为vue是根据文件名进行加载的。
-
npm run serve
运行服务会自动加载.env.development
开发环境配置文件 -
npm run build
运行构建会自动加载.env.production
生产环境配置文件
运行npm run serve
启动服务命令时,若在vue.config.js
核心中配置了devServer
开发服务器选项的proxy
代理设置后,开发环境中会使用proxy
代理服务器访问接口数据。但正式生产环境下此选项会失效。因为此时vue会读取.env.production
环境配置,而忽略vue.config.js
中的devServer
选项。
环境配置自定义
通过在package.json
的scripts
配置项中添加 --mode xxx
来选择不同环境。
$ vim package.json
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit",
"lint": "vue-cli-service lint"
}
"scripts": {
"serve": "vue-cli-service serve --mode development",
"build": "vue-cli-service build --mode development",
"test:unit": "vue-cli-service test:unit --mode development",
"lint": "vue-cli-service lint --mode development"
},
项目配置vue.config.js
vue.config.js
是一个可选的配置文件,若项目根目录中存在此文件,则会被@vue/cli-service
自动加载。
vue-cli 3.x 创建的目录下看不到WebPack配置。手工配置webpack可在根目录下创建vue.config.js
文件。在根目录下创建vue.config.js
文件作为Vue项目配置文件,其中配置输出路径名、根目录、预处理、devServer配置、PWA、DLL、第三方插件等。
$ vim vue.config.js
module.exports = {
configureWebpack:config=>{
if(process.env.NODE_ENV === "production"){
}else{
}
}
};
配置选项
选项 | 描述 |
---|---|
publicPath | 部署应用宝的基本URL,和webpack的output.publicPath一致。 |
outputDir | 运行vue-cli-service build 时生成的生产环境构建文件的目录,目标目录在够坚强会被清除。 |
assetsDir | 静态资源目录 |
indexPath | 指定生成的index.html 的输出路径 |
filenameHashing | 是否在生成的静态资源文件名中包含hash以控制缓存 |
pages | 在多页模式下构建应用,每个页面对应一个入口文件。 |
lintOnSave | 是否在开发环境下通过eslint-loader在每次保存时lint代码 |
runtimeCompiler | 是否使用包含运行时编译器的vue构建版本 |
CSS Pre-processors
VUE CLI支持CSS Modules、PostCSS和SASS、LESS、Stylus在内的预处理器。所有编译后的CSS会通过 css-loader 来解析其中的url()
引用,并将应用作为模块请求来处理。这意味着可以根据本地文件结构使用相对路径引用静态资源。若要应用NPM以来中的文件,或使用webpack alias,则需在路径前添加 ~
前缀来避免歧义。
TypeScript
安装TypeScript
$ npm i -S typescript
$ npm i -S @vue/cli-plugin-typescript
配置TypeScript
若目录下存在tsconfig.json
文件则意味着该目录是TypeScript项目的根目录,tsconfig.json
文件中指定了用来编译项目的根文件和编译选项。项目可使用tsconfig.json
来编译。
- 当不带任何输出文件的情况下调用
tsc
文件,编译器会从当前目录开始查找tsconfig.json
文件,并逐级向上搜索父级目录。 - 当不带任何输出文件时调用
tsc
文件且使用命令行参数--project
或-p
指定一个包含tsconfig.json
文件的目录。 - 当在命令行上指定了输入文件时,
tsconfig.json
文件会被忽略。
根目录下创建tsconfig.json
,默认情况下typescript只负责静态检查,即使遇到错误也仅仅在编译时报错并不会中断编译,最终还是会生成一份JS文件,如果想要在报错时终止JS文件的生成,可在tsconfig.json
配置中设置noEmitOnError
选项为true
。
$ vim tsconfig.json
{
//编译选项
"compilerOptions": {
//编译目标版本
"target": "esnext",
//指定模块系统
"module": "esnext",
//是否启用严格类型检查
"strict": true,
//在.tsx文件中支持JSX
"jsx": "preserve",
//从tslib导入辅助工具函数
"importHelpers": true,
//模块处理方式,默认classic
"moduleResolution": "node",
//是否启用实验性的ES装饰器
"experimentalDecorators": true,
//报错时不生成输出文件
"noEmitOnError":true,
//通过为所有导入创建名称空间对象,支持CommonJS和ES模块之间的互操作性。意味着allowSyntheticDefaultImports。
"esModuleInterop": true,
//是否允许从没有设置默认导出的模块中默认导入,仅用于类型检查。
"allowSyntheticDefaultImports": true,
//是否生成map文件
"sourceMap": true,
//工作根目录
"baseUrl": ".",
//指定引入的类型声明文件,默认自动引入所有声明文件,一旦指定则会禁用自动引入,只引入指定的类型。
"types": [
"webpack-env",
"mocha",
"chai"
],
//指定模块的路径,和baseUrl有关联,和webpack中resolve.alias配置一样。
"paths": {
"@/*": [
"src/*"
]
},
//编译过程中需引入的库文件列表
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
//指定匹配列表,属于自动指定该路径下所有TS相关文件。
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
//指定排除列表,include的反向操作。
"exclude": [
"node_modules"
]
}
错误处理
$ npm run serve
> sxyh_web_stats@0.1.0 serve D:\vue\workspace\sxyh_web_stats
> vue-cli-service serve
INFO Starting development server...
ERROR WebpackOptionsValidationError: Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry[2] should be a string.
-> A non-empty string
WebpackOptionsValidationError: Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry[2] should be a string.
-> A non-empty string
例如:针对多页应用的项目配置
$ vim vue.config.js
const path = require("path");
const fs = require("fs");
// const glob = require("glob");
const pxtoviewport = require("postcss-px-to-viewport");
//是否开发调试模式
const debug = process.env.NODE_ENV === "development" ? true : false;
//获取文件路径
const joinPath = (...args)=>path.join(__dirname, ...args);
/*获取配置*/
const config = (filename,field="")=>{
let value = require(joinPath("config", filename));
if(field!==""){
value = value[field];
}
return value;
};
/*获取多页面配置选项*/
const getPages = ()=>{
let pages = {};
//获取pages目录下所有文件夹,即每个单页。
fs.readdirSync(joinPath("src", "pages")).forEach(dirname=>{
//生成应用组件文件
const app = joinPath("src", "pages", dirname, "App.vue");
if(!fs.existsSync(app)){
//todo 读取模板内容 替换内容后写入
let code = `
<template>
<router-view/>
</template>
<script>
export default {
name:"app"
}
</script>
<style scoped>
</style>
`;
fs.writeFileSync(app, code);
}
//生成路由文件
const router = joinPath("src", "pages", dirname, "router.js");
if(!fs.existsSync(router)){
let code = `
import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);
export default new Router({routes:[
]});
`;
fs.writeFileSync(router, code);
}
//生成入口文件
const entry = joinPath("src", "pages", dirname, "main.js");
if(!fs.existsSync(entry)){
let code = `
import Vue from "vue";
import Axios from "axios";
import App from "./App.vue";
import router from "./router.js";
Vue.config.productionTip = false;
Vue.prototype.axios = Axios;
new Vue({render:h=>h(App), router}).$mount("#${dirname}");
`;
fs.writeFileSync(entry, code);
}
//单页配置选项
const template = "index.html";
const filename = `${dirname}.html`;
const chunks = ['chunk-vendors', 'chunk-common', dirname];
const chunksSortMode = "manual";
const minify = false;
const inject = true;
//自定义页面数据
const pageData = config("page", dirname) || {};
if(pageData.title === undefined){
Object.assign(pageData, {title:dirname});
}
if(pageData.idname === undefined){
Object.assign(pageData, {idname:dirname});
}
pages[dirname] = {entry, template, filename, pageData, chunks, chunksSortMode, minify, inject};
});
return pages;
};
module.exports = {
publicPath:debug?"/":"",
outputDir:"dist",
assetsDir:"assets",
filenameHashing:true,
lintOnSave:!debug,
runtimeCompiler:!debug,
pages:getPages(),
configureWebpack:config=>{
const extensions = [".js", ".json", ".vue", ".css"];
const alias = {
"@":path.join(__dirname, "src"),
"src":path.join(__dirname, "../src"),
"assets":path.join(__dirname, "../src/assets"),
"components":path.join(__dirname, "../src/components")
};
config.resolve = {extensions, alias};
},
css:{
loaderOptions:{
postcss:{
plugins:[
pxtoviewport({
unitToConvert:"px",
unitPrecision:3,
viewportWidth:750,
viewportUnit:"vw",
fontViewportUnit:"vw",
minPixelValue:1,
mediaQuery:false,
replace:true,
propList:["*"],
selectorBlackList:[],
exclude:/(\/|\\)(node_modules)(\/|\\)/,
landscape:false,
landscapeUnit:"vh",
landscapeWidth:1334
})
]
}
}
},
//开发服务器
devServer:{
//设置代理
proxy:{
"/api":{
target:"http://127.0.0.1:4000",
ws:false,
changeOrigin:true
}
}
}
};
启动入口
Vue2实例启动入口文件默认为main.js
$ vim src/pages/index/main.js
import Vue from 'vue';
import Axios from "axios";
import App from './App.vue';
import router from "./router.js";
Vue.config.productionTip = false;
Vue.prototype.axios = Axios;
Vue.prototype.apiUrl = process.env.VUE_APP_API_URL;
Vue.prototype.debug = process.env.NODE_ENV === "development";
//创建vue应用实例并挂在到#index元素上
const vm = new Vue({render:h=>h(App), router:router});//未挂载状态
//手工挂载vm实例
vm.$mount('#index');
这里运用了Vue2新增的Render方法,为了得到更好地运行速度,Vue2也采用了Virtual DOM虚拟DOM技术。Virtual DOM是一种比浏览器原生DOM性能更好的虚拟组件模型。
const vm = new Vue({render:h=>h(App), router:router});//未挂载状态
通过import
将Vue.js文件引入后创建Vue实例对象,在Vue实例中使用Render方法来绘制App这个Vue组件以完成初始化。
vm.$mount('#index');//手工挂载vm实例
将Vue实例绑定到页面中ID为index的元素上,这样App Vue程序就引导成功了。
一个Vue实例必须与一个页面元素绑定,Vue实例一般用作Vue的全局配置来使用。
$ vim public/index.html
<% const page = htmlWebpackPlugin.options.pageData; %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- 在 head 标签中添加 meta 标签,并设置 viewport-fit=cover 值 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
<!-- 开启 safe-area-inset-bottom 属性 -->
<van-number-keyboard safe-area-inset-bottom />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<title><%= page.title %></title>
<style>
body{
margin: 0;
overflow-x: auto;
color: #323233;
font-size: 16px;
font-family: PingFang SC, 'Helvetica Neue', Arial, sans-serif;
background-color: #f7f8fa;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body>
<noscript>
<strong>很抱歉,如果没有启用javascript,vue-cli3无法正常工作。请启用它以继续。</strong>
</noscript>
<div id="app">
<div id="<%= page.idname %>"></div>
</div>
</body>
</html>
单页组件
Vue2默认Vue组件文件为App.vue
,*.vue
是Vue特色的文件格式表示是一个Vue组件,是Vue特色又被称为单页式组件。*.vue
文件同时承载视图模板、样式定义和组件代码。
$ vim src/pages/index/App.vue
<template>
<router-view/>
</template>
<script>
export default {
name: "app",
components:{},
data(){
return {
};
},
created(){
//console.log(this.$route);
},
methods:{
}
}
</script>
<style scoped>
</style>
Vue的组件系统提供了一种抽象,使用独立可复用的组件来构建大型应用。因此,几乎任意类型应用的界面都可以抽象为一个组件树。
单页组件由三部分组成
-
<template>
视图模板 -
<script>
组件定义 -
<style>
组件样式表