Vue2 渐进式前端框架开发

11/1/2020 vue

# Vue2 简述

# vue的特点

遵循MVVM模式 编码简洁,体积小,运行效率高,适合移动/PC端开发 他本身只关注UI,可以轻松引入vue插件或其他库开发项目

# Vue 基本架构

动态的html页面,包含了一些JS语法代码,双括号表达式。指令(以V-开通的自定义标签属性)

<div id="box">	<!-- v-model:双向数据绑定 -->
	<input type="text" v-model="message" />
	<p>	 <!-- {{}}单向数据绑定 -->
	  Hello	{{message}}
	</p> 
</div>
<!-- 1.引入Vue.js -->
<script src='js/vue.js'></script>
<!-- 2.创建Vue对象 -->
<script>
	const vm = new Vue({
        el: '#box',
        data: {
            message: 'Hello Vue'
            // message为初始化数据,可在vue元素容器中使用
        }
    })
</script>

# Vue 开发者工具调试

安装扩展:VueDevtools

TSGJ

# MVVM概念

title:MVVM模型
Note left of View: DOM
View->DOM Listeners:访问监听器(get,set)
DOM Listeners->Model(obj):查找数据
note over DOM Listeners,Data Bindings:ViewModel
Model(obj)->Data Bindings:数据绑定调用
Data Bindings-->View:进行DOM绑定监听数据

**View:**视图,模板页面 **Model:**模型,数据对象(data) **ViewModel:**视图模型(vue的实例)

# Vue 基础命令&对象

# Vue-html指令

  1. v-text:更新元素的textContent
  2. v-html:更新元素的innerHTML
  3. v-if:如果为true, 当前标签才会输出到页面
  4. v-else:如果为false, 当前标签才会输出到页面
  5. v-show:通过控制display 样式来控制显示/隐藏
  6. v-for:遍历数组/对象
  7. v-on:绑定事件监听, 一般简写为@
  8. v-bind:标签属性绑定解析表达式, 可以省略v-bind
  9. v-model:双向数据绑定
  10. ref:指定唯一标识, vue 对象通过$els 属性访问这个元素对象
  11. v-cloak:防止闪现, 与css 配合: [v-cloak] { display: none }
  12. created()/mounted(): 发送ajax请求, 启动定时器等异步任务
  13. beforeDestory(): 做收尾工作, 如: 清除定时器

# 标签属性绑定数据(v-bind)

完整写法:v-bind:xxx='yyy' yyy会被作为表达式解析执行 简洁写法::xxx='yyy'

<div id="app">
	<img src="imgUrl" /><br />	        <!-- 报错 -->
	<img v-bind:src="imgUrl" /><br />		<!-- 显示 -->
	<img :src="imgUrl" />            		<!-- 显示 -->
</div> 
<script>
    ew Vue({
	el:"#app",
	data:{
		imgUrl: "https://cn.vuejs.org/images/logo.png"
	 }
})
</script>

# 标签绑定事件(v-on)

完整写法:v-on:事件名='fun' fun是Vue实例定义的方法 简洁写法:@事件名='fun'

<div id="app">
	<button v-on:click="test">点击</button>
	<button @click="test('abc')">点击</button>
</div>
<script>
 new Vue({
	el:"#app",
	methods:{
		test(a) {
			a.isTrusted ? alert("我没传入形参啦") : alert(a);;
		}
	}
})
</script>

# 条件渲染样式

v-if v-else v-show

比较v-if与v-show 如果一些不怎么切换的地方用v-if 因为隐藏的元素会直接去除 如果需要频繁切换的使用v-show 因为**隐藏的元素会加上display: none;**而不是直接去除

<style>
    .aClass{color:#0000FF;}
</style>
<div id="demo">
	<p v-if="bool">成功了</p>
	<p v-else>失败了</p>
    
    <!-- 或者是下面代码 -->
    <p v-show="bool">表白成功</p>
	<p v-show="!bool">表白失败</p>
	<button @click="bool=!bool">点击</button>
</div>
<script>
var vm =new Vue({
	el:'#demo',
	data:{
		bool:false
	}
})
</script>

# 类名绑定Vue数据(:class)

解析表达式为字符串时,绑定对应的Vue数据。

<style>
    *{margin: 0;padding: 0;}
	.aClass{color: blue; }
	.cClass{color: red; }
</style>
<div id="demo">
	<p class="cClass" :class="text">xxx是字符串</p>
	<button @click="classEco">点击更改class</button>
</div>
<script>
var vm =new Vue({
	el:"#demo",
	data:{text:'aClass'},
    // 类名数据绑定aClass类名
	methods:{
		classEco(){this.text = 'bClass'}
        // 类名数据变为bClass类名
	}
})
</script>

解析表达式为数组时,绑定数组元素对应的类名。

<style>
	.aClass{color: blue; }
	.bClass{background: red; }
</style>
<div id="demo">
		<p :class="['aClass','cClass']">xxx是数组</p>
</div>

解析表达式为对象时,绑定表达式对象内 类名对应Vue数据。

当对应Vue数据为true,表示显示该类名,false则删除该类名

<style>
	.aClass{color: blue; }
	.bClass{color: red; }
	.cClass{font-size: 30px;line-height: 50px;}
</style>
<div id="demo">
	<p class="cClass" :class="{aClass:isA,bClass:isB}">xxx是对象</p>
</div>
<script>
var vm =new Vue({
	el:"#demo",
	data:{isA:true,isB:false},
	methods:{
		classEco(){this.isB=true;this.isA=false}
    }
})
</script>

# style标签属性绑定Vue数据(:style)

解析表达式类型:对象,数组,Vue数据名

数组和Vue数据名并不常用,所以只举一个对象的例子

解析表达式为对象时,绑定表达式对象内 css属性名对应Vue数据

<div id="demo">
	<p :style="{color:ftCol,fontSize:ftSz +'px'}">style绑定</p>
	<button @click="classEco">点击更改style</button>
</div>
<script>
var vm =new Vue({
	el:"#demo",
	data:{ftCol:'red',ftSz:20},
	methods:{
		classEco(){this.ftCol='green';this.ftSz=60}
    }
})
</script>

# 标签列表渲染(v-for)

<!--语法: v-for="(item , index) in Vue数组数据"-->
<p v-for="(item,index) in persons" :key="index">		
		{{index}}---{{item.name}}---{{item.age}}    
<p>
<script>
var vm = new Vue({
    el: '#demo',
    date: {
        persons: [
			{name: 'Tom', age:18},
			{name: 'Jack', age:17},
			{name: 'Bob', age:19},
			{name: 'Mary', age:16}
        ]
    }
})
</script>

如果执行v-for命令,需要添加一个:key='xxx'标签以便它能跟踪每个节点的身份,从而重用和重新排序现有元素。

因为它是 Vue 识别节点的一个通用机制,key 并不仅与 v-for 特别关联。 不能使用对象或数组之类的非基本类型值作为 v-for 的 key。要用不一样的字符串或数值类型的值。

# 渲染列表增删改
  <div id="demo">
  	<ul>
   		<li v-for="(p,index) in persons" :key="item.id" >
			{{index}}---{{p.name}}---{{p.age}}
			---<button @click="deletP(index)">删除</button>
			---<button @click="undataP(index,{name:'Cat',age:20})">更新</button>
   		</li>
  	</ul>
 	<button @click="">添加</button>
</div>
<script>
var vm = new Vue({
el: '#demo',
    data: {
      persons: [{name: 'Tom', age:18},{name: 'Jack', age:17}]
    },
    methods:{
		deletP(index){this.persons.splice(index,1) },
        // vue只是监视了persons的改变,没有监视数组内部属性的改变
        // 所以当直接改变数组时,数据并不会进行同步
        // 但Vue重写了数组中一系列改变数组内部的方法,所以当调用数组方法时,会改变数据
        // (先调用元素,更新界面) > 使用变异数组内部方法 > 数据绑定 > 页面变化
		undataP(index,obj){this.persons.splice(index,1,obj)}
	}
})
</script>

# Vue 内置对象

# 计算属性值 (computed 对象)

# computed 函数 计算属性
<div id="demo">
	姓:<input type="text" v-model="firstName"/><br />
	名:<input type="text" v-model="lastName"/><br />
	姓名(单向):<input type="text" v-model="Name" />
</div>
<script>
new Vue({
	el: "#demo",
	data:{firstName: 'Aaa',lastName: 'bbbB'},
	computed:{
		name (){
			//什么时候执行: 初始化显示/相关的data属性数据发生了改变
			return this.firstname + " " + this.lastname 
		}
	}
})
</script>
# computed 对象 监视读写
<div id="demo">
	姓:<input type="text" v-model="firstName"/><br />
	名:<input type="text" v-model="lastName"/><br />
	姓名(双向):<input type="text" v-model="name" /><!--firstName + lastName-->
</div>
<script>
new Vue({
	em: "#demo",
	data:{firstname: 'Aaa',lastname: 'bbbB',}, 
	computed:{
		funllname: {
			get(){
                // 回调函数,当需要读取当前属性值时回调,根据相关的数据计算并返回当前属性的值
				return this.firstname + "|" + this.lastname
			},
			
			set(value){
                // 回调函数,当属性值发生改变时回调,更新相关的属性数据
                // value就是funllname的最新属性值
				var names = value.split("|")
				names.length == 1 ? names[1] = ":" : []
				this.firstname = names[0]
				this.lastname = names[1]
			}
		},
	}
})
</script>

# 监视属性值 (watch 对象)

# Vue 对象内部监视
<div id="demo">
	姓:<input type="text" v-model="firstName"/><br />
	名:<input type="text" v-model="lastName"/><br />
	姓名(单向):<input type="text" v-model="funllname" />
    			<!--firstName + lastName-->
</div>
<script>
var vm = new Vue({       
		el: "#demo",
		data:{firstName: 'Aaa',lastName: 'bbbB',funllname: ''},
		watch:{ 
		firstName: function (value){ 
            // 当firstName值改变时执行,初始化不会执行
			this.funllname = value + ' ' + this.lastname
		},
    this.$nextTick(()=>{
  	// 页面数据更新后执行
	  })
})

# Vue 对象外部监视
vm.$watch('lastName',function(newVal){ // 当lastName值改变时执行,初始化不会执行
	this.funllName = this.firstName + ' ' + newVal    
 })
</script>

# Vue 切换显示动画

Vue transition 允许我们只设置隐藏或者显示的样式, 这样切换的时候, 也会有过渡的效果

<transition name="myAnim"><div v-show="fool">666</div></transition>
.[name]-enter-active, .[name]-leave-active{/*显示/隐藏的过渡样式*/}
.[name]-enter, .[name]-leave-to {/*隐藏的样式*/}
.[name]-enter-top, .[name]-leave {/*显示的样式*/}

# Vue 模板占位符

<view>
  <text class="title">{{title}}</text>
  <template v-for="(item, index) in 6">
    <div :key="index">{{item}}</div>
  </template>
</view>

# Vue 事件修饰符

<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

# Vue 按键修饰符

<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">
.enter	.tab	.delete (捕获“删除”和“退格”键)	.esc	.space	.up	.down	.left	.right

# Vue 模板过滤器

// 定义过滤器
Vue.filter("dateFileter", (value, ...args)=> {
    return "..." // ...
})
<!-- 字符串模板使用过滤器 -->
<time>{{time | dateFileter}}</time>
<!-- 命令模板使用过滤器 -->
<div v-bind:time="time | dateFileter"></div>

# Vue 实例对象生命周期

# 生命周期流程

Vue 实例生命周期

# Vue生命周期函数

一、初始化显示可调用函数

  • beforeCreate()
  • created()
  • beforeMount()
  • mounted()

二、更新状态可调用函数:this.xxx = value

  • beforeUpdate()
  • updated()

三、销毁Vue实例可调用函数:vm.$destory()

  • beforeDestory()
  • destoryed()

一般比较常用的生命周期方法

  • created()/mounted(): 发送ajax请求, 启动定时器等异步任务
  • beforeDestory(): 做收尾工作, 如: 清除定时器

# Vue 自定义插件

# 自定义标签属性

upper-text 是自定义标签属性名,这个值可以是任何值 el 是调用此方法的 html 标签 binding 是这个标签所包含的内容

# Vue所有实例注册

Vue.directive('upper-text', function(el, binding){
    el.innerText = binding.value.toUpperCase()
    // 元素内所有字符串改变为大写
})

# Vue单个实例注册

new Vue({
	el:'#test',
	data:{ msg2:'Dzl SB?,,,,Yes!!'},
	directives:{ // 注册局部指令:只在当前vm管理范围有效
		'upper-text':function(el,binding){
			el.innerText=binding.value.toLowerCase()// 全部转为大写
		}
	}
})

# 使用自定义标签属性

<div id="test">
	<p v-upper-text='msg'> </p>
</div>

# Vue 自定义插件

# 定义自定义插件

vue-myPlugin.js

import Vue from 'Vue'
const Muplugin = {// 插件对象必须有一个install()
    install: function (Vue, options){
		// 添加全局方法或属性
		Vue.ZdyWenFun = function (x){console.log(x)}
		// 添加全局自定义标签属性
		Vue.directive('my-upper',function(el,binding){
			el.innerText = binding.value.toUpperCase()
		})
 	}
}
export d Muplugin
// 添加Vue对象实例方法
Vue.prototype.$myMethod = function(){console.log('我tm是实例方法')}

# 调用自定义插件

<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript" src="js/vue-myPlugin.js"></script>
<script type="text/javascript">
  Vue.use(Muplugin) // 内部进行调用解析插件对象的install()
  const vm = new Vue({
    el: '#test',
    data: {msg: 'i LOve U'},
    //添加的实例方法
	mounted() {this.$myMethod()}
  })
  
  // 添加的全局方法 可以在任何地方调用
  Vue.ZdyWenFun('全局方法阿nmd');
  // 添加的实例方法 只能在当前的vm实例中调用
  vm.$myMethod()
</script>

# Vue 获取后台数据

# axios

// 在需要使用的组件内引入axios
import axios from 'axios'
// 使用axios发送ajax请求获取数据
axios.get(url).then(axioData => {
    console.log(axioData.data)
}).catch(axioErrorData => {
    console.log(axioErrorData.data)
})

# vue-resource

/* Main.js入口函数设置 */
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-resource' 
Vue.use(VueRouter)
var vm = new Vue({ el: '#app', components: { App }, template: '<App/>' })
/* App.vue实例中使用  */
mounted () { 
    // 入口函数设置后Vue实例会自带一个$http对象
	this.$http.get(url).then(
   		// 获取成功则调用这个函数
   		resourData => { console.log(axioData.data) },
   		// 获取失败则调用这个函数
   		resourErrorData => { console.log(resourErrorData.data) }
	)
}

# Vue 源码分析

# 相关JS技术

# 伪数组转换真数组

<li>test1</li><li>test2</li><li>test3</li>
<script>
    const lis = document.querySelectorAll('li') // 这是一个伪数组 {0:li, 1:li....}
    // ES5 转换为真数组	[0:li, 1:li....]
	const lisAll = Array.prototype.slice.call(lis)
    // ES6 转换为真数组	[0:li, 1:li....]
	const lisAll2 = Array.from(lis) 
</script>

# 获取&判断类型节点

const el = document.getElementById('test') // 获取元素节点
const attrNode = el.getAttributeNode('id') // 获取标签节点
const textNode = el.firstChild // 获取文本节点

// 判断节点类型
console.log(el.nodeType, attrNode.nodeType, textNode.nodeType)
// 元素节点返回 1 标签节点返回 2 文本节点返回 3

# 对象属性描述符

// 创建对象方法的get和set语句
const obj = {
  firstName: 'A', lastName: 'B',
  get fullName () { // 定义数据描述符
    return this.firstName + '-' +this.lastName
  },
  set fullName (val) {
    const names = value.split('-')
		this.firstName = names[0]
		this.lastName = names[1]
  }
}

**语法:**Object.defineProperty(obj , prop , { descriptor })

obj: 要在其上定义属性的对象。 **prop:**要定义或修改的属性的名称。 **descriptor:**将被定义或修改的属性描述符。

defineProperty 自定义描述符达到数据绑定与数据同步

const obj = { firstName: 'A', lastName: 'B' }
// 给obj添加一个 fullName 其绑定firstName和lastName的数据
Object.defineProperty(obj, 'fullName', {
	get: function () {
        // 定义数据描述符
		return this.firstName + '-' +this.lastName
	},
	set: function (value) {
        // 定义存取描述符
		const names = value.split('-')
		this.firstName = names[0]
		this.lastName = names[1]
	}
})

defineProperty 对象其他描述符

Object.defineProperty(obj, 'fullName2', {
    // 是否可改变定义特性
	configurable: true, 
    // 是否可枚举
	enumerable: true, 
    // 初始值 可以是任何有效的javascript值
	value: 'fullName2222' , 
    // 是否可修改
	writable: false
})

# 获取对象属性名数组

const ObjNames = Object.keys(obj)
// ObjNames-->['firstName', 'lastName', 'fullName']

# 属性名判断是否是obj自身属性

// 语法:obj.hasOwnProperty(prop)
console.log(obj.hasOwnProperty('firstName'))

# DocumentFragment (批量更新节点)

documentFragment:用于储存节点的容器,并且容器是在内存隔离的,不与外界发生关系

用documentFragment一次性修改所有li

<ul>
	<li>test1</li>
	<li>test2</li>
	<li>test3</li>
</ul>
<script>
    const ul = document.querySelector('ul')
    
    // 1. 创建fragment
    const fragment = document.createDocumentFragment()
    
    // 2. 取出ul中所有子节点取出保存到fragment
    let child // 当appendChild操作时,会把child清空
	while (child = ul.firstChild) {fragment.appendChild(child)}
    
    // 3. 更新fragment中的所有li文本
    // 这里也可以直接获取所有的元素节点,这样就不用过滤了
    [].slice.call(fragment.childNodes)..forEach(node => {
        // 过滤不是元素节点的节点
		if (node.nodeType === 1) { node.innerText = 'WWWW'}
	})
    
    // 4. 将fragment插入ul
    ul.appendChild(fragment)
</script>

# Vue 面向组件编程

# 脚手架搭建环境(vue/cli-v2)

npm i vue vue-router vue-cli -g		## 安装全局脚手架构建工具

vue init webpack '项目名字'  ------>

Project name '文件名' 				#[,是否为项目名,默认是]
Project description '项目描述'
Author '作者名,邮箱地址'
Vue build '打包工具名字??'
Install vue-router? (Y/n)   					#[,是否安装路由 ]
Use ESLint to lint your code (Y/n)      		#[,是否使用ESLint管理代码 ]
Pick an ESLint preset (Use arrow keys) (Y/n)		#[,选择一个ESLint预设 ]
Setup unit tests with Karma + Mocha? (Y/n)		#[,是否安装单元测试 ]
Setup e2e tests with Nightwatch(Y/n)? 		#[,e2e测试 ]

#↓↓↓↓↓↓↓#

npm run dev ## 内存中打包并开启服务

#项目文件配置run dev时自动打开浏览器
#.config --> index.js --> autoOpenBrowser : true

# 项目目录文件解析

## 项目总文件
  - Build 			webpack相关配置
  - config	 		vue基本配置文件(监听端口,打包输出等相关配置)
  - node_modules		用node安装的依赖包
  - src				资源文件夹以后我们就在这个目录写代码
  - static			静态资源(图片之类)json数据之类
  - test				单元测试,代码测试
  - .babelrc			ES6语法编译配置,依赖将es6代码转换为浏览器识别的代码
  - .editorconfig		定义代码格式
  - .gitignore		上传需要忽略的文件格式
  - .postcssrc.js		转换css工具
  - index.html		页面入口
  - npm-debug.log		npm相关log信息
  - package.json		项目基本信息(项目开发所需模块,项目名称,版本)
  - readme.md			项目说明(如何使用,有哪些方法等等)
## src目录文件 > 项目资源
  - assets		    静态资源(js,css之类可以放在这下面)
  - components	    公用组件编写的地方
  - App.vue		    项目的主组件,所有页面都是在app.vue下切换的.一个标准的vue文件,分为三部分。
  - main.js	    	页面程序入口文件,加载各种公共组件
## Build目录文件 > webpack配置
  - Build.js		       生产环境构建代码
  - check-versions.js     检查node、npm等版本
  - dev-client.js		   热重载相关
  - dev-server.js		   构建本地服务器
  - utils.js			   构建工具相关
  - vue-loader.conf.js  	css加载器配置
  - webpack.base.conf.js	webpack基础配置
  - webpack.dev.conf.js	webpack开发环境配置
  - webpack.prod.conf.js	webpack生产环境配置
  - webpack.test.conf.js	测试相关配置
## config目录文件 > vue-cli配置
  - dev.env.js	开发环境变量
  - index.js		项目一些配置变量
  - prod.env.js	生产环境变量
  - test.env.js	测试环境变量

# 生产环境的操作

## 编译打包
npm run build
## 模拟后台 (静态服务器工具包)
安装:npm install serve -g
运行dist包文件夹:serve dist
访问: http://localhost:5000

## 修改打包项目名称
webpack.prod.conf.js --> output:{publicPath: '打包名称'}

## 动态web服务器如何开启打包项目
将打包文件拷贝到运行的tomcat(后端服务) 的运行目录下
访问: http://localost:8080/xxx

# 代码规范检测

eslint它定义了很多特定的规则, 一旦你的代码违背了某一规则, `eslint` 会作出非常有用的提示
EsLint的检测范围有`ES``JSX``Style`。还可以自定义错误和提示
规则的错误等级有三种:
- 0:关闭规则。
- 1:打开规则,并且作为一个警告(信息打印黄色字体)
- 2:打开规则,并且作为一个错误(信息打印红色字体)
eslint规则配置通常在.eslintrc.js的rules对象中, 在这里可以关闭某些规则
// https://eslint.org/docs/user-guide/configuring

module.exports = {
  root: true,
  parserOptions: {
    parser: 'babel-eslint',
    sourceType: 'module'
  },
  env: {
    browser: true
  },
  // https://github.com/standard/standard/blob/master/docs/RULES-en.md
  extends: [
    'plugin:vue/base'
  ],
  // required to lint *.vue files
  plugins: [
    'vue'
  ],
  // add your custom rules here
  'rules': {
    // allow paren-less arrow functions
    'indent': [2, 2], // 两个空格的缩进
    'quotes': [2, 'single'], // js必须使用单引号
    "linebreak-style": [0 ,"error", "windows"], //允许windows开发环境
    // 'semi': [2, 'always'], // 语句强制分号结尾
    'no-console': [1], // 不允许console语句
    'no-unused-vars': [1], // 声明了变量但是没有使用检测
    'space-unary-ops': [1, { 'words': true, 'nonwords': false }], // 一元运算符的前/后要不要加空格
    'brace-style': [2, '1tbs', { 'allowSingleLine': false }], // 大括号风格
    'comma-spacing': [2, { 'before': false, 'after': true }],   // 逗号后有空格,前没有空格
    'comma-style': [2, 'last'],  // 逗号跟在结尾
    'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }], // 对象字面量中冒号的前后空格
    'lines-around-comment': [ // 行前/行后备注
      2, {
        'beforeBlockComment': false, // 段注释的前后
        'beforeLineComment': false, // 行注释的前面
        'afterBlockComment': false, // 块注释的后面
        'afterLineComment': false, // 行注释的后面
        'allowBlockStart': true,
        'allowObjectStart': true,
        'allowArrayStart': true
      }],
    'max-depth': [2, 4], // 代码最多允许4层嵌套
    'max-len': [1, 160, 2],
    'max-nested-callbacks': [2, 3], // 回调嵌套深度
    'max-params': [2, 5], // 函数最多只能有5个参数
    'max-statements': [1, 80],  // 单个函数最多80条语句
    'no-array-constructor': [2], // 禁止使用数组构造器
    'no-lonely-if': 2, // // 禁止else语句内只有if语句
    'no-multiple-empty-lines': [2, { 'max': 3, 'maxEOF': 1 }], // 空行最多不能超过2行
    'no-nested-ternary': 2,  // 不使用嵌套的三元表达式
    'no-spaced-func': 2, // 函数调用时 函数名与()之间不能有空格
    'no-trailing-spaces': 2, // 一行结束后面不要有空格
    'no-unneeded-ternary': 2, // 禁止不必要的嵌套 var isYes = answer === 1 ? true : false;简单的判断用三元表达式代替
    'object-curly-spacing': [2, 'always', { // 大括号内是否允许不必要的空格 always始终允许;never始终不允许
      'objectsInObjects': false,
      'arraysInObjects': false
    }],
    'arrow-spacing': 2, // =>的前/后括号
    'block-scoped-var': 2, // 块语句中使用var
    'no-dupe-class-members': 2,
    // 'no-var': 1, // 禁用var,用let和const代替
    'object-shorthand': [1, 'always'], // 强制对象字面量缩写语法
    'array-bracket-spacing': [2, 'never'], // 是否允许非空数组里面有多余的空格
    'operator-linebreak': [2, 'after'], // 换行时运算符在行尾还是行首
    'semi-spacing': [2, { 'before': false, 'after': true }], // 分号前后空格
    'keyword-spacing': ['error'],
    'space-before-blocks': 2, // 不以新行开始的块{前面要不要有空格
    'block-spacing': [2, 'always'],
    'space-before-function-paren': [2, 'never'], // 函数定义时括号前面要不要有空格
    'space-in-parens': [2, 'never'], // 小括号里面要不要有空格
    'spaced-comment': [1, 'always',
      { 'exceptions': ['-', '*', '+']
      }], // 注释风格要不要有空格什么的
    'arrow-parens': 0,
    // allow async-await
    'generator-star-spacing': 0,
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
  },
  globals: {
    '$': false,
    'jquery': false,
    'ActiveXObject': false,
    'arbor': true,
    'layer': false
  }
};

# 脚手架搭建环境(vue/cli@3/4)

cnpm i @vue/cli -g

vue create  '项目名'  / vue ui

#↓↓↓↓↓↓↓#

Vue CLI v4.3.1
? Please pick a preset: (Use arrow keys)	## 选择你的配置, 如果之前保存有配置, 会在此显示
  default (babel, eslint)		## 默认配置
> Manually select features	## 手动配置

#↓↓↓↓↓↓↓#

? Check the features needed for your project: ## 选择配置项, <空格>表示选择, <a>表示全选, <i>表示反转
>(*) Babel	## ES6转为ES5的解析器
 ( ) TypeScript		## .ts的解析器
 ( ) Progressive Web App (PWA) Support		## 渐进式Web应用程序
 (*) Router		## vue路由
 (*) Vuex		## vue状态数据管理
 (*) CSS Pre-processors		## css预编译器
 (*) Linter / Formatter		## 代码风格检查和格式化
 (*) Unit Testing		## 单元测试(unit tests)
 ( ) E2E Testing		## e2e测试(end to end)
 
#↓↓↓↓↓↓↓#
 
? Check the features needed for your project: Babel, Router, Vuex, CSS Pre-processors, Linter, Unit
? Use history mode for router? (Y/n) 	## router是否使用history模式, 否则使用hash默认(建议n)

#↓↓↓↓↓↓↓#

? Pick a CSS pre-processor (选择css 预处理器):
  Sass/SCSS (with dart-sass)
  Sass/SCSS (with node-sass)
> Less
  Stylus
  
#↓↓↓↓↓↓↓#

? Pick a linter / formatter config: ## 选择Eslint 代码验证规则 (通常Prettier用的比较多)
  ESLint with error prevention only
  ESLint + Airbnb config
  ESLint + Standard config
> ESLint + Prettier

#↓↓↓↓↓↓↓#

? Pick additional lint features: ## 选择什么时候检测
>(*) Lint on save		## 保存就检测
 ( ) Lint and fix on commit		## fix或commit的时候检测
 
#↓↓↓↓↓↓↓#

? Pick a unit testing solution: ## 选择单元测试方案
> Mocha + Chai		## Mocha测试库+Chai断言库
  Jest		## Jest测试库

#↓↓↓↓↓↓↓#

? Where do you prefer placing config for Babel, ESLint, etc.? ## 项目配置文件存放处
  In dedicated config files		## 独立文件存放
> In package.json		## 统统放在package.json中

#↓↓↓↓↓↓↓#

? Save this as a preset for future projects? (y/N) ## 是否保存该配置到本地文件, 如果选择Y, 选择需要输入名称

#↓↓↓↓↓↓↓#

Vue CLI v4.3.1		## 安装相应包, 等待创建项目中
✨  Creating project in D:/......
⚙️  Installing CLI plugins. This might take a while...

#↓↓↓↓↓↓↓#

npm run serve --open	## 内存中打包并开启服务

## 配置run serve自动打开浏览器
package.json --> scripts:{serve: "vue-cli-service serve --open"}

# 项目目录文件解析

## 项目总文件
  - node_modules		用node安装的依赖包
  - src				资源文件夹以后我们就在这个目录写代码
  - public			静态资源html(图片之类)json数据之类
  - tests				单元测试,代码测试
  - .gitignore		上传需要忽略的文件格式
  - babel.config.js		babel相关log信息
  - package.json		项目基本信息(项目开发所需模块,项目名称,版本, es配置)
  - README.md			项目说明(如何使用,有哪些方法等等)
## src目录文件 > 项目资源
  - assets		    静态资源(js,css之类可以放在这下面)
  - components	    公用组件编写的地方
  - router/index.js		路由配置文件
  - store/index.js		vuex状态数据管理配置文件
  - views			路由组件存放地(视图组件)
  - App.vue		    项目的主组件,所有页面都是在app.vue下切换的.一个标准的vue文件,分为三部分。
  - main.js	    	页面程序入口文件,加载各种公共组件

# 生产环境的操作

## 编译打包
npm run build
## 模拟后台 (静态服务器工具包)
安装:npm install serve -g
运行dist包文件夹:serve dist
访问: http://localhost:5000

## 修改打包项目名称
webpack.prod.conf.js --> output:{publicPath: '打包名称'}

## 动态web服务器如何开启打包项目
将打包文件拷贝到运行的tomcat(后端服务) 的运行目录下
访问: http://localost:8080/xxx

# 代码规范检测

eslint它定义了很多特定的规则, 一旦你的代码违背了某一规则, `eslint` 会作出非常有用的提示
EsLint的检测范围有`ES``JSX``Style`。还可以自定义错误和提示
规则的错误等级有三种:
- 0:关闭规则。
- 1:打开规则,并且作为一个警告(信息打印黄色字体)
- 2:打开规则,并且作为一个错误(信息打印红色字体)
eslint规则配置通常在.eslintrc.js的rules对象中, 在这里可以关闭某些规则

# 脚手架配置项

module.exports = {

    // 项目部署的基础路径
    // 我们默认假设你的应用将会部署在域名的根部,
    // 比如 https://www.my-app.com/
    // 如果你的应用时部署在一个子路径下,那么你需要在这里
    // 指定子路径。比如,如果你的应用部署在
    // https://www.foobar.com/my-app/
    // 那么将这个值改为 `/my-app/`
    // baseUrl: '/Reader/dist/',  /*这个是我存放在github在线预览的项目*/

    // 将构建好的文件输出到哪里
    outputDir: 'dist',

    // 放置静态资源的地方 (js/css/img/font/...)
    assetsDir: '',

    // 用于多页配置,默认是 undefined
    pages: {
        index: {
            // 入口文件
            entry: 'src/main.js',  /*这个是根入口文件*/
            // 模板文件
            template: 'public/index.html',
            // 输出文件
            filename: 'index.html',
            // 页面title
            title: '度衡量'
        },
        // 简写格式
        // 模板文件默认是 `public/subpage.html`
        // 如果不存在,就是 `public/index.html`.
        // 输出文件默认是 `subpage.html`.
        subpage: 'src/main.js'    /*注意这个是*/
    },

    // 是否在保存的时候使用 `eslint-loader` 进行检查。
    // 有效的值:`ture` | `false` | `"error"`
    // 当设置为 `"error"` 时,检查出的错误会触发编译失败。
    lintOnSave: true,

    // 使用带有浏览器内编译器的完整构建版本
    // 查阅 https://cn.vuejs.org/v2/guide/installation.html#运行时-编译器-vs-只包含运行时
    runtimeCompiler: true,

    // babel-loader 默认会跳过 node_modules 依赖。
    // 通过这个选项可以显式转译一个依赖。
    transpileDependencies: [/* string or regex */],

    // 是否为生产环境构建生成 source map?
    productionSourceMap: true,

    // 调整内部的 webpack 配置。
    // 查阅 https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli/webpack.md
    chainWebpack: () => { },
    configureWebpack: () => {
        // resolve: {
        //     alias: {
        //       // 别名
        //       vue$: "vue/dist/vue.esm.js" //加上这一句
        //     }
        //   }
    },
    // CSS 相关选项
    css: {
        // 将组件内的 CSS 提取到一个单独的 CSS 文件 (只用在生产环境中)
        // 也可以是一个传递给 `extract-text-webpack-plugin` 的选项对象
        extract: true,

        // 是否开启 CSS source map?
        sourceMap: false,

        // 为预处理器的 loader 传递自定义选项。比如传递给
        // sass-loader 时,使用 `{ sass: { ... } }`。
        loaderOptions: {
            less: {
                prependData: `@import "@/assets/css/main.less";`//@指向src目录 
            }
        },

        // 为所有的 CSS 及其预处理文件开启 CSS Modules。
        // 这个选项不会影响 `*.vue` 文件。
        modules: false
    },

    // 在生产环境下为 Babel 和 TypeScript 使用 `thread-loader`
    // 在多核机器下会默认开启。
    parallel: require('os').cpus().length > 1,

    // PWA 插件的选项。
    // 查阅 https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli-plugin-pwa/README.md
    pwa: {},

    // // 配置 webpack-dev-server 行为。
    // devServer: {
    //     open: process.platform === 'darwin',
    //     host: '0.0.0.0',
    //     port: 8080,
    //     https: true,
    //     hotOnly: false,
    //     // 查阅 https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli/cli-service.md#配置代理
    //     proxy: null, // string | Object
    //     before: app => { }

    // },

    // 三方插件的选项
    pluginOptions: {
        // ...
        
    },  // 增加一个plugins

    
}

# 组件基本架构

# 入口函数 src / main.js

/* src/main.js */
import Vue from 'vue'     	 //创建vue实例
import App from './App.vue'  //引入主要组件
var vm = new Vue({
 	el: '#app',				//选择ID为app的元素
	components: {App},		//映射组件为标签
 	 template: '<App />'		//app元素内部添加映射标签<App />
})

# 主组件格式 src / app.js

<template>
    <!-- 模板页面(内容必须用div包裹) -->
	<div></div>
</template>

<script>
    // JS 模块对象
	export default {
		data() {return {}},
		methods: {},
		…………
	}
</script>

<style>
    /* 样式定义 */
</style>

# 引入组件格式

	<template>
	<HelloWorld></HelloWorld>		<!-- 3- 使用组件标签 -->	
	<hello-world />					<!-- 3- 使用组件标签 -->
	</template>
	<script>
    /* 1- 引入组件 */
	import HelloWorld from './components/HelloWorld'     
	export default {
		components: {HelloWorld}/* 2- 映射成标签 */
	}
	</script>

# 父子组件间通信

# 子组件接收父组件数据

利用标签名从父组件传输数据到子组件

<!-- 父组件标签(App.js)传输数据(任意JS属性或方法) -->
<TdoHead  :addTask='addTask' /> 
<!--  子组件(components/...js)props接收数据  -->
<script>
 const vm = new Vue({
     props: { addTask: Function }
 })
</script>

利用自定义事件传输父组件方法到子组件

<!-- 父组件标签(App.js)传输对应方法 -->
<TdoHead  @addTask='addTask' /> 
<script>
mounted () { 
      // 定义自定义方法
      this.$refs.header.$on('addTask', this.addTask)
}
</script>
<!--  子组件(components/...js)利用$emit方法接收父组件方法  -->
<script>
     methods: {
      sumTask () { 
		// 使用自定义事件,this.$emit('事件名', [,传入的形参])
         this.$emit('addTask', '666')
      }
 	}
</script>

# 子组件solt接受父组件标签

一、父组件利用子组件标签,传入实体标签

<!-- 传入子组件需要的标签 -->
<template> 
	<div slot="xxx">xxx 对应的标签结构</div>
	<div slot="yyy">yyy 对应的标签结构</div>
</template> 

二、子组件使用父组件传入的标签输出标签

<!-- 输出父组件标签 -->
<template>
	<slot name="xxx">父组件对应xxx的标签结构</slot>
	<div>组件确定的标签结构</div>
	<slot name="yyy">父组件对应yyy的标签结构</slot>
</template>

# 父组件接受slot子组件属性 / 事件

一、子组件利用 slot 插槽传递任意属性 / 事件

<!-- 输出父组件标签, 传入参数 -->
<template>
	<slot name="xxx" :toggle="addCount" :count="count"></slot>
</template>

<script setup lang="ts">
export default {
  data: () => ({
    count: 0,
  }),
  methods: {
    addCount() {
      this.count++
    }
  }
}
</script>

二、父组件从插槽中获取属性 / 事件

<!-- 传入子组件需要的标签, 接收参数 -->
<template> 
    <!-- slot-scope接收参数(废弃) -->
	<div slot="xxx" slot-scope="{ count, toggle }" @click="toggle"> {{ count }} </div>
    <!-- v-slot接收参数 -->
    <div v-slot:xxx="{ count, toggle }" @click="toggle"> {{ count }} </div>
</template> 

# 子组件间通信

# props通信

  1. 父组件定义状态数据 父组件定义改变状态数据方法
  2. 父组件传递状态数据给子组件B 父组件传递改变状态数据方法给子组件A
  3. 子组件A调用方法改变父组件状态数据 子组件B自动调用componentWillReceiveProps()方法并接收状态数据

# 消息订阅系统

  1. 引入消息订阅系统 import PubSub from 'pubsub-js'
  2. 发布消息 PubSub.publish('消息名',data)
  3. 订阅消息(当消息发送改变时执行,并接收数据) PubSub.subscribe('消息名',(msg, data){...})

# 组件引入css库

# 安装css库包

cnpm i stylus-loader stylus --save-dev or cnpm i less-loader less --save-dev or cnpm i sass-loader sass --save-dev

# style中使用

<style lang='less'>
</style>
<style lang='stylus'>
</style>

# 路由组件编程

单页 Web 应用(single page web ),整个应用只有一个完整的页面,点击页面中的链接不会刷新页面, 本身也不会向服务器发请求,当点击路由链接时, 只会做页面的局部更新,数据都需要通过 ajax 请求获取, 并在前端异步展现。

单页面应用

# Vue 路由管理器

Vue Router 是 Vue.js (opens new window) 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌!!!

# 定义路由组件

路由组件装载着需要的内容,通常这类组件放在 src/views 文件夹内(About.vue、Home.vue)

# 定义路由控制器

// src/rouder/index.js:
import Vue from 'vue' 
import VueRouter from 'vue-router'  
import About from '../views/a.vue'
import Home from '../views/b.vue'
// 对路由插件进行解析
Vue.use(VueRouter) 
// 这里定义一个路由
export default new VueRouter({
	linkActiveClass: 'active', // 定义默认路由类名
  routes = [
      // 配置路由组件地址
      { path: "/about",component: About,children: [
          //{ path:'note', component: aboutl } //其他子路由
        ], meta: {} // $route元数据 router.meta
      },
      { path: "/home", component: Home },
      // 配置默认显示路由
      { path: "/", redirect: "/about" }
  ];
})

# Vue 入口函数引入路由

import Vue from 'vue'
import App from './App.vue'
import router from './router'
const app = new Vue({
	components: { App },
	template: '<App/>',
    // 将设置好的路由器传入组件
	router
}).$mount('#app')

# 静态组件使用路由组件

<div id="app">
	<router-link to="/about">Go to about</router-link>
	<router-link to="/home">Go to home</router-link>
	<router-view> </router-view>
</div>

1574341010

# 路由组件缓存

可让显示路由保存值和自身,在刷新页面时或重新启动浏览器不会消失

<keep-alive include="test-keep-alive"> <!-- 利用keep-alive标签包裹显示路由 -->
	<router-view class="w"></router-view>
</keep-alive>

# 组件中操作路由

# 组件实例访问路由

mounted () {
    // 当前显示路由
	console.log(this.$route) 
    // 路由器
	console.log(this.$router) 
}

# 静态组件跳转路由链接

new Vue({
    ...
    this.$router.push('路由链接'),
  	this.$router.replace('路由链接')
    ...
})

# 路由跳转时传入参数

# 路径中传入占位符参数(params)

// 定义路由时, 定义占位符为id
roates: [{ path: '/home/:id', commponent: Home }]
<!-- 定义标签内跳转, 传入id为6 -->
<router-link :to="/home/6">User</router-link>
/* 定义命令跳转, 传入id为6 */
this.$router.push("/home/6")

# 对象中传入对象参数(params)

<!-- 定义标签内跳转, 传入id为6 -->
<router-link :to="{path: '/home', params: {id: 60}}">User</router-link>
/* 定义命令跳转, 传入id为6 */
this.$router.push({path: "/home", params: {id: 60}})

# 对象中传入查询参数(query)

<!-- 定义标签内跳转, 传入id为6 -->
<router-link :to="{path: '/home', query: {id: 60}}">User</router-link>
/* 定义命令跳转, 传入id为6 */
this.$router.push({path: "/home", query: {id: 60}})

# 监听路由参数变化

# 使用watch进行监听

/* 定义路由组件接受变化 */
watch: {
    // 全局监听
    $route (to, form) {},
    // 单独监听
    "$route.params.id" (to, form) {}
}

# 使用 beforeRouteUpdate 导航守卫

const User = {
  template: '...',
  beforeRouteUpdate (to, from, next) {
    // 对路线变化做出反应...
    // 不要忘记调用 next()
  }
}

# 导航守卫(路由拦截器)

# 全局前置守卫

使用 router.beforeEach 注册一个全局前置守卫:

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。具体参数参考:

# 全局后置钩子

可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

router.afterEach((to, from) => {
  // ...
})

# 路由独享守卫

在路由配置上直接定义 beforeEnter 守卫:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

# 组件内的守卫

可以在路由组件内直接定义以下路由导航守卫:

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

# Vuex 集中式状态管理

# Vuex 基本概念

Vuex 是一个专为 Vue.js 应用程序开发的集中式状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

# 基本状态管理模式

new Vue({
  // state 数据
  data () {return{count: 0}},
  // view  视图
  template: `<div>{{ count }}</div>`,
  // actions 改变数据方法
  methods: {
	increment () {this.count++}
  }
})
Viem-->Actions:调用方法
Actions-->State:寻找数据
State-->Viem:页面显示数据

state:驱动应用的数据源 view:以声明方式将 state 映射到视图 actions,响应在 view 上的用户输入导致的状态变化。

# 多组件数据共享状态问题

  • 多个视图依赖于同一状态
  • 来自不同视图的行为需要变更同一状态
  • 以前的解决办法
    1. 将数据以及操作数据的行为都定义在父组件
    2. 将数据以及操作数据的行为传递给需要的各个子组件(有可能需要多级传递)
participant 子组件D
父组件A->子组件B:传输数据
父组件A->子组件D:传输数据
# Vuex的解决方法

将数据以及操作数据的行为分别管理,任意组件都可以随意调用并修改

participant 子组件B
participant Vue状态管理 as vx
vx->父组件A:传输数据
vx->子组件B:传输数据

# Vuex总流程图

Vuex流程图

# 什么情况下应该使用 Vuex?

Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。

如果不打算开发大型单页应用,不必使用 Vuex 管理。如果应用足够简单,最好不要使用 Vuex 。一个简单的 store 模式 (opens new window) 就足够了。但是,如果需要构建一个中大型单页应用,就要更好地在组件外部管理状态,这是 Vuex 就很有必要性了。

# Vuex 基本架构

安装:cnpm i --save vuex

# 简单store状态管理

# 1. 定义核心管理模块
// src / store.js
import Vue from 'vue'
import Vuex from 'vuex'
// vue解析vuex插件
Vue.use(Vuex)
const state = {} // 状态对象
const mutations = {} // 包含多个更新state函数的对象
const actions = {} // 包含多个对应事件回调函数的对象
const getters = {} // 包含多个getter计算属性函数的对象
// 向Vuex添加所有管理对象(必须),名称必须统一(state, mutations, actions, getters)
export default new Vuex.Store({ state, mutations, actions, getters })
# 2. 入口js引入
import Vue from 'vue'
import App from './App.vue'
import store from './store.js'
var vm = new Vue({
  	el: '#app',
  	render: h => h(app),
  	store // 所有的组件对象都多了一个属性:$store
})

# 模块化状态管理

# 1. 定义文件目录结构(src / store)
  • state.js / 状态对象 /
  • actions.js / 多个更新state函数的引用对象 /
  • mutations.js / 多个对应actions的回调函数的对象 /
  • getters.js / 多个getter计算属性函数的对象 /
  • index.js / 集合所有store对象的集合与管理对象 /
# 2. 定义Vuex接口 (src / store / index.js)
/* vuex的核心管理入口模组 */
import Vue from 'vue'
import Vuex from 'vuex'

import state from './state.js'
import mutations from './mutations.js'
import actions from './actions.js'
import getters from './getters.js'
Vue.use(Vuex)
export default new Vuex.Store({ state, mutations, actions, getters })

# Vuex 模块规划

# 定义多模块

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}
const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}
const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

# 模块的局部状态

const moduleA = {
  state: () => ({
    count: 0
  }),
  mutations: {
    increment (state) {
      // 这里的 `state` 对象是模块的局部状态
      state.count++
    }
  },
  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  }
}

同样,对于模块内部的 action,想要获取另一个模块的状态或者计算属性,通过以下方式引入。

const moduleA = {
  // ...
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState,rootGetters  }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  }
}

# Vuex 组件中使用

# 组件显示state数据内容

/* 状态数据(state.js) */
state = { cliNum: 0 }
<!-- 任意组件 -->
<div>{{ $store.state.cliNum }}</div>

# 组件使用计算值

/* 状态数据(getters.js) */
getters = {
  	type (state) {
   		return (state.cliNum % 2) === 0 ? '偶数' : '奇数'
  	}
}
<!-- 任意组件 -->
<div>{{ $store.getters.type }}</div>

# 组件中调用actions函数

# 只传入单一形参
// actions.js
actions = {
  	qaq ({commit}, Arg) { commit('QAQ'), Arg }
}
// mutations.js
mutations = {
  	QAQ (state, Arg) { console.log('我运行啦' + Arg) }
}
<!-- 任意组件 -->
<button @click="$store.dispatch('qaq', [a,b,c])">+</button>
# 以数组方式传入多个形参( Vuex )
// actions.js
actions = {
  	qaq ({commit}, Args) { commit('QAQ',{Arg1:Args[0], Arg2:Args[1]})  }
}
// mutations.js
mutations = {
  	QAQ (state, {Arg1, Arg2}) { console.log(Arg1,Arg2) } 
}
// 任意组件
mounted () {
	this.$store.dispatch('qaq', ['wd','nmd']) // 控制台输出'wdnmd'
}
# 以数组方式传入多个形参( AcMutations )
/* 状态数据(acMutions.js) */
qaq (state, {arg1, arg2}) {
    console.log(Arg1,Arg2)
}
mounted () {
	this.$store.dispatch('qaq', ['wd','nmd']) // 控制台输出'wdnmd'
	}

# 组件引入store和actions

<script>
  import { mapState, mapGetters, mapActions } from 'vuex'
  export default {
    computed: {
      // 获取动态数据的地方 mapState执行结果是对象
      // 在把这个对象属性给computed,computed会对该属性的属性描述符进行改造,并把属性添加到实例的this中
      ...mapState(['cliNum']),
      ...mapGetters(['type'])
    },
    methods: {
      // 获取actions方法的地方
      ...mapActions(['plus', 'reduce', 'plusOdd', 'oneSecPlus'])
    }
  }
</script>

# ElementUI的使用

# vue-cli@2 文件中引入

npm i element-ui -S ## 安装
npm install babel-plugin-component -D ## 按需引入插件

#↓↓↓.babelrc↓↓↓#

{
  "presets": [["es2015", { "modules": false }]],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

页面中引入

import Vue from 'vue';
import { Button, Select } from 'element-ui';
import App from './App.vue';
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
/* 或写为
 * Vue.use(Button)
 * Vue.use(Select)
 */
new Vue({
  el: '#app',
  render: h => h(App)
});

# vue-cli@3/4 文件中引入

vue create my-app	## vue-cli创建项目
cd my-app		## 进入项目文件

vue add element		## 添加element插件
  Installing vue-cli-plugin-element...

#↓↓↓↓↓↓↓#

? How do you want to import Element? #你想如何导入Element
> Fully import		## 完全导入
  Import on demand		## 按需导入
  
? Choose the locale you want to load (zh-CN)	## 选择语言
	Invoking generator for vue-cli-plugin-element...
	Installing additional dependencies...

# vue-cli@3/4 UI中引入

# 自定义主题样式

## 在线主题编辑器编辑样式
  1. https://element.eleme.cn/#/zh-CN/theme
  2. 下载自定义样式压缩包
  3. 解压到项目文件--> my_app --> theme
## 搭配插件按需引入组件主题

  修改babel.config.js配置 --> plugins -->
  "styleLibraryName": "theme-chalk" 	-->		"styleLibraryName": "~theme"

# plugins 插件配置文件

// 当用vue add 或者vue-cli ui添加完element插件后
// 会在src中多出一个plugins的插件配置文件
// 里面包括了所有第三方插件的配置, element的配置也在其中
// plugins/element.js的配置主要作用是按需引入需要的组件
import Vue from "vue";
import { Button } from "element-ui";
Vue.use(Button);

# .vue 组件中使用

<template>
  <div>
    <el-button>Click Me</el-button>
  </div>
</template>

<script>
	export default {}
</script>

<style>
</style>

# VeeValidate的使用

vee validate 一个轻量级的 vue (opens new window)表单验证插件。基于模板的验证既熟悉又易于设置。验证html (opens new window)输入和vue (opens new window)组件,生成本地化错误,可扩展,它可以完成所有操作。

安装:npm i vee-validate --save

# 入门使用

// ValidationProvider是验证提供者, 该组件通过作用域插槽为模板提供验证错误。
// VeeValidate不附带任何验证规则, 由extend函数添加验证规则
import { ValidationProvider, extend } from 'vee-validate';
// vee-validate/dist/rules是vee-validate官方提供暴露的多种验证规则
import { required, email } from 'vee-validate/dist/rules';
// 添加必填规则
extend('required', required);
// 添加邮箱规则
extend('email', email);
// 注册验证提供者组件, 组件通过作用域插槽为模板提供验证错误
Vue.component('ValidationProvider', ValidationProvider);
<!-- 
rules为规则字符串表达式, 用|分开为多个规则同时共用
v被称为槽的道具, 它可以让类似的组件ValidationProvider将信息发送到该插槽, 
这里可以使用ES6的对象扩展, 使事情变得更简洁
-->
<ValidationProvider rules="required|email" v-slot="{ errors }">
  <input v-model="email">
  <p>{{ errors[0] }}</p>
</ValidationProvider>

v插槽更多功能:https://logaretm.github.io/vee-validate/guide/state.html#flags 第三方UI库使用:https://logaretm.github.io/vee-validate/guide/3rd-party-libraries.html#element-ui

# 自定义规则

# 基本使用语法

import { extend } from 'vee-validate';
// extend函数接受规则的名称以及用于该规则的验证函数和其他参数。
extend('positive', {
  validate(value) {
    return value >= 0;
  },
  // 字符串占位符, 占位符内容由ValidationProvider组件name参数提供
  message: '{__field__}不能为空'
});
<ValidationProvider rules="positive" v-slot="{ errors }">
  <input v-model="value" type="text">
  <span>{{ errors[0] }}</span>
</ValidationProvider>

# 单参数使用

import { extend } from 'vee-validate';
// 使用验证规则的扩展形式并定义params包含参数名称的属性
// args发送给该validate方法的第二个参数是一个对象,其中包含在params数组中指定的键。
extend('min', { // 接收参数, 定义最小值规则
  validate(value, args) {
    return value.length >= args.length;
  },
  params: ['length']
});
<!-- 模板中使用(规则字符串表达式) -->
<ValidationProvider rules="required|min:3" v-slot="{ errors }">
  <input v-model="value" type="text">
  <span>{{ errors[0] }}</span>
</ValidationProvider>
<!-- 模板中使用(对象表达式) -->
<ValidationProvider rules="{required:true,min:3}" v-slot="{ errors }">
  <input v-model="value" type="text">
  <span>{{ errors[0] }}</span>
</ValidationProvider>

# 多参数使用

import { extend } from 'vee-validate';

extend('minmax', {
  validate(value, {length}) {
    return length >= args.min && length <= args.max;
  },
  params: ['min', 'max']
});

# 本土化设置

vee-validate具有40多个可用于交付的验证的语言环境,但是默认情况下不会安装它们,因为它们的开销很大,因此需要导入所需的语言环境。公开的localize帮助程序使您可以向验证消息中添加新的语言环境

import { localize } from 'vee-validate';
import en from 'vee-validate/dist/locale/en.json';
import zh_CN from 'vee-validate/dist/locale/zh_CN.json';
// 安装英语与中文简体配置.
localize({ en, zh_CN });

更多配置:https://logaretm.github.io/vee-validate/guide/localization.html#using-the-default-i18n

# 验证提供者(ValidationProvider)

ValidationProvider组件是包装您的输入并使用作用域插槽 (opens new window)提供验证状态的组件。

这些是插槽范围内可用的属性,可通过以下方式访问v-slot

Name Type Description
errors string[] 错误消息列表。
failedRules [x: string]: string 失败规则的映射对象,其中 (rule, message) 为 (key, value)
aria { [x: string]: string } 为可访问性映射aria属性的对象。
classes { [x: string]: boolean } 根据验证状态配置的类的映射对象。
validate (e: any) => Promise 用作事件处理程序以触发验证的函数。对于不使用v模型的字段很有用。
reset () => void 重置提供程序上的验证状态的函数。
valid boolean|undefined 如果该字段有效。
invalid boolean|undefined 如果该字段无效。
pristine boolean 如果从未操纵该字段值。
dirty boolean 如果字段值已被操纵。
pending boolean 指示是否正在进行字段验证。
required boolean 如果该字段为必须项。
validated boolean 如果该字段已至少验证一次。
passed boolean 如果该字段已经过验证并且有效。
failed boolean 如果该字段已经过验证并且无效。

由于插槽作用域可以利用ES6扩展的优势,因此您可以选择加入其中的任何属性,并在认为合适时传递到插槽模板

<ValidationProvider rules="required" v-slot="{ errors }">
  <div>
    <input type="text" v-model="value">
    <span>{{ errors[0] }}</span>
  </div>
</ValidationProvider>

# 验证观察者(ValidationObserver)

ValidationObserver是你的包装形式和使用它在嵌套的所有字段提供汇总的验证状态的组件范围的插槽 (opens new window)

由于ValidationObserver的solt插槽属性基本与验证提供者(ValidationProvider)一直,这里只挑一些重点内容。

Name Type Description
validate () => Promise<boolean> 为所有提供程序触发验证的方法。除非“silent”为true,否则更改子程序状态。
handleSubmit (cb: Function) => Promise<void> 像validate一样调用验证并更改提供程序的状态,只接受在验证成功时才运行的回调。
reset () => void 为所有提供程序重置验证状态的方法。
<ValidationObserver v-slot="{ handleSubmit }">
  <!-- 当所有表单完成时, 提交才会调用onSubmit方法 -->
  <form @submit.stop.prevent="handleSubmit(onSubmit)">
    <!-- 该项可有多个ValidationProvider组件组成的input -->
  </form>
</ValidationObserver>
<ValidationObserver v-slot="{ passed }">
  <!-- 当提交时, 调用onSubmit传入表单验证状态 -->
  <form @submit.stop.prevent="onSubmit(passed)">
    <!-- 该项可有多个ValidationProvider组件组成的input -->
  </form>
</ValidationObserver>

# Jest 测试VeeValidate

jest.config.js

transform: {
    ...
  'vee-validate/dist/rules': 'babel-jest',
},
transformIgnorePatterns: [
  '<rootDir>/node_modules/(?!vee-validate/dist/rules)',
]

comp.vue

<ValidationProvider rules="required" v-slot="{ errors }" ref="provider">
  <input v-model="value" type="text">
  <span class="error">{{ errors[0] }}</span>
</ValidationProvider>

comp.test.js

// 创建环境
const wrapper = mount(MyComponent, { sync: false });
// 查找input, 设置值未空
wrapper.find('input').setValue('');
// 刷新, 挂起验证
await flushPromises();
// 从参考中获取错误消息
const error = wrapper.vm.$refs.provider.errors[0];
// 验证错误
expect(error).toBeTruthy();

# 完整实例

import Vue from 'vue'
// 引入验证组件, 汇总验证状态组件, 验证添加工具, 语言环境设置
import {
  ValidationProvider,
  ValidationObserver,
  extend, localize
} from 'vee-validate';
import * as rules from "vee-validate/dist/rules";
import zh_CN from "vee-validate/dist/locale/zh_CN.json";
// 进行按需引入规则
['required', 'email', 'min', 'max'].forEach(key => {
  extend(key, rules[key])
})

// 本土化设置
// localize('zh_CN', zh_CN)
localize({zh_CN})
// 全局安装VeeValidate组件
Vue.component("ValidationObserver", ValidationObserver);
Vue.component("ValidationProvider", ValidationProvider);
<template>
  <validation-observer ref="observer" v-slot="{ passed }">
    <form @submit.stop.prevent="onSubmit(passed)">
      <!-- 设置名称表单与验证 -->
      <validation-provider :rules="{required:true, min:3}" v-slot="{ error }">
        <input v-model="name" type="text">
        <span>{{error[0]}}</span>
      </validation-provider>
      <!-- 设置邮箱表单与验证 -->
      <validation-provider rules="required|email" v-slot="{ error }">
        <input v-model="email" type="text">
        <span>{{error[0]}}</span>
      </validation-provider>
    </form>
  </validation-observer>
</template>
<script>
export default {
  data: ()=> ({
    name: "",
    email: ""
  }),
  methods: {
    // 当表单提交时调用, 接收验证状态
    onSubmit(passed){
      if (!passed) return console.log('表单项未通过验证');
    }
  }
}
</script>

# Vue 常见问题

# vue-cli@4 常用配置

// my_app/vue.config.js(默认未创建)
module.exports = {
    outputDir: 'dist',   //build输出目录
    assetsDir: 'assets', //静态资源目录(js, css, img)
    lintOnSave: false, //是否开启eslint
    devServer: {
        open: true, //是否自动弹出浏览器页面
        host: "localhost", 
        port: '8081', 
        https: false,   //是否使用https协议
        hotOnly: false, //是否开启热更新
        proxy: null, // 代理
    }
}

# vue-cli@4 解决跨域

module.exports = {
    devServer: {
        proxy: { // 代理
          '/api': {
                target: '<url>', //API服务器的地址
                ws: true,	//代理websockets
                changeOrigin: true,	// 虚拟的站点需要更管origin
                pathRewrite: { '^/api': '' }//重写路径 比如'/api/aaa/ccc'重写为'/aaa/ccc'
          	},
         }
      }
}

# vscode 关闭自动添加分号和转换单引号

"vetur.format.defaultFormatterOptions": {
  "js-beautify-html": {
    // force-aligned | force-expand-multiline
    "wrap_attributes": "force-aligned"
  },
  "prettyhtml": {
    "printWidth": 100,
    "singleQuote": false,
    "wrapAttributes": false,
    "sortAttributes": true
  },
  "prettier": {
      "semi": false,
      "singleQuote": true
  }
},

# TypeScript 中使用 eslint 和 Prettier 的配置

.eslintrc.js


module.exports = {
     /* 指定如何解析语法。可以为空,但若不为空,只能配该值,原因见下文。*/
    parser: 'vue-eslint-parser', 
    /* 扩展配置,加一些插件 */            
    extends: [
        'plugin:vue/recommended',              /* eslint应用在vue的必须配置 */
        'plugin:prettier/recommended'          /* 使用Prettier */
    ],
    /* 优先级低于parse的语法解析配置 */
    parserOptions: {
        parser: '@typescript-eslint/parser',   /* 解析ts语法 */
        ecmaVersion: 2018,
        sourceType: 'module'
    }
}