Vue2全家桶
参考文档及链接
相关命令
安装环境相关
下载nodejs并安装:https://nodejs.org/en/download/
配置淘宝镜像:npm config set registry https://registry.npm.taobao.org
全局安装脚手架:npm install -g @vue/cli
关于全局命令无法生效的解决方案:https://blog.csdn.net/qq_45955580/article/details/116745061
脚手架相关
创建Vue项目:vue create project_name
其它
查看具体的webpack配置:vue inspect > output.js
事件
组件标签
本身没有预留事件,只有常见的标签(button、input等)身上有,如果想要对组件绑定 click
事件,则需要使用自定义事件(:click="click"),再调用 $emit('click', ()=>{})
方法。
事件类型
事件的种类,会在我不断接触的过程中,一个一个补全。
1. 单击事件:`@click`
2. 双击事件:`@dblclick`
3. 滚动事件:`@scroll`
4. 鼠标按下:`@keydown`
5. 鼠标松开:`@keyup`
6. 失去焦点:`@blur`
7. 获得焦点:`@focus`
一般事件修饰符
1. `.stop`:阻止单击事件继续传播(阻止冒泡);
2. `.prevent`:阻止默认事件的触发;
3. `.capture`:在事件正常流程开始执行之前进行捕捉,被捕捉的事件立即执行;
4. `.self`:只当在 event.target 是当前元素自身时触发处理函数;
5. `.once`:仅执行一次。
滚动条专属修饰符(:scroll)
1. `.passive`:滚动事件专属,滚动会优先于事件处理执行,即界面优先响应滚动。
.passive
和 .prevent
不能一起使用。
鼠标按钮修饰符(:click)
1. `.left`:鼠标左键按下触发;
2. `.right`:鼠标右键按下触发;
3. `.middle`:鼠标滚动键按下触发。
普通键盘修饰键(:keyup)
1. `.enter`:按下 Enter 键触发;
2. `.tab`:按下 Tab 键触发;
3. `.delete`:按下 Delete 或 Backspace 键触发;
4. `.esc`:按下 Esc 键触发;
5. `.space`:按下空格键触发;
6. `.up`:按下[上]方向键触发;
7. `.down`:按下[下]方向键触发;
8. `.left`:按下[左]方向键触发;
9. `.right`:按下[右]方向键触发;
999. `.键名`:用 `$event.key` 获取按键的名称,如:`PageDown`,然后使用 `@keyup.page-down` 触发按键事件;
系统键盘修饰键(:keydown)
1. `.ctrl`
2. `.alt`
3. `.shift`
4. `.meta`:Win键,或Command键。
如:v-on:keyup.alt.67
表示 Alt
+ Enter
组合键;v-on:click.ctrl
表示 Ctrl
+ 鼠标左键
组合键。
组合按键修饰符
1. `.exact`:被修饰的按键修饰键被优先按下时才会触发事件,还可以用于限制仅有一个按键按下(没有其他任何按键按下)时触发。
数据联动
v-model
v-model
只对具有 输入
属性的页面元素有效,如:input
。
使用 v-model
时,触发对象是 输入元素
,连带触发对象是 使用该绑定变量的所有页面元素
。
<!DOCTYPE html>
<html lang="en">
<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">
<title>Demo</title>
</head>
<body>
<div id="app">
<input type="text" v-model="content" @focus="content = ''">
<h1>{{ content }}</h1>
<h2>{{ content }}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data() {
return {
content: '正在等待输入...'
}
},
})
</script>
</body>
</html>
watch
监听属性,被监听的是 变化的值
,主动监听必须要有一个载体,可以是一个变量,也可以是持久存储方法。
本例中使用 result
作为监听变化的载体。
<!DOCTYPE html>
<html lang="en">
<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">
<title>Demo</title>
</head>
<body>
<div id="app">
<input type="text" v-model.float="x"> <span>* 2 + 1 =</span> <span>{{ result }}</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data() {
return {
x: '',
result: '' // 作为监听变化的载体
}
},
watch: {
x: {
deep: false, // 深度监听,默认 false
immediate: false, // 立即触发回调,默认 false
handler(val){
this.result = (val * 2 + 1).toString()
}
}
// 当不使用 deep 和 immediate 两个属性时,可以简写成
// x(val){
// this.result = (parseFloat(val) * 2 + 1).toString()
// }
}
})
// 或者使用 $watch 添加监听
// const unwatch = vm.$watch('x', function(val){
// this.result = (val * 2 + 1).toString()
// }, {deep: false, immediate: false}) // 第三个参数可以省略
// unwatch() // 取消监听
</script>
</body>
</html>
computed
计算属性(计算方法)不需要预先定义存储对象(不需要预先在 data
属性中声明),它是作为构建方法名调用的。
<!DOCTYPE html>
<html lang="en">
<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">
<title>Demo</title>
</head>
<body>
<div id="app">
<input type="text" v-model.float="x"> <span>* 2 + 1 =</span> <span>{{ result }}</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data() {
return {
x: '',
}
},
computed: {
result: {
get(){
return (this.x * 2 + 1).toString()
},
set(val){
// 设置函数,略
}
}
// 当仅使用 get() 方法时,等价于(简写)
// result(){
// return (this.x * 2 + 1).toString()
// }
}
})
</script>
</body>
</html>
组件间通信
这里假设您对组件
和脚手架
已经有了初步理解。若您无组件基础,此笔记对您无效。
父对子传
方法一:props
父组件【./App.vue】
父组件 App
通过引用子组件 DemoHeader
,并在HTML元素中实例化子组件 <DemoHeader/>
,实现对子组件的使用。
通过向子组件标签传递参数 :title="title"
完成对子组件的传值。此处的 "title"
为数据绑定传值。
<template>
<div id="app">
<DemoHeader :title="title"/>
</div>
</template>
<script>
import DemoHeader from './components/DemoHeader.vue'
export default {
name: 'App',
components: {
DemoHeader,
},
data(){
return {
title: "Demo-Title"
}
}
}
</script>
子组件【./components/DemoHeader.vue】
子组件 DemoHeader
用 props
属性接收来自父组件(通过标签属性)传递的值。
此处的 title
为父组件传参的参数名称(即父组件标签传值的 :title
中的 title
)。
<template>
<div>
<h1>{{ title }}</h1>
</div>
</template>
<script>
export default {
name: 'DemoHeader',
props: ['title']
}
</script>
子对父传&任意传
子对父传,不可能直接传,只能间接传。
(Vue2.0数据操作原则:数据在哪个组件,操作就在哪个组件里)
方法一:props
可以通过向 子组件
传递一个函数,然后 子组件
通过调用此函数,并向此函数传递参数的方式
,实现 子组件
向 父组件
传递数据的目的。
父组件【./App.vue】
向子组件传递 changeTitle
方法。
<template>
<div id="app">
<DemoHeader :title="title" :changeTitle="changeTitle"/>
</div>
</template>
<script>
import DemoHeader from './components/DemoHeader.vue'
export default {
name: 'App',
components: {
DemoHeader,
},
data(){
return {
title: "Demo-Title"
}
},
methods:{
changeTitle(msg){
this.title = msg
}
}
}
</script>
子组件【./components/DemoHeader.vue】
通过 props
属性,接收父组件传递过来的函数 changeTitle
,并通过新的方法 changeHeaderTitle
完成参数调用。
<template>
<div>
<h1>{{ title }}</h1>
<button @click="changeHeaderTitle('你好,父组件!')">Change Title</button>
</div>
</template>
<script>
export default {
name: 'DemoHeader',
props: ['title', 'changeTitle'],
methods: {
changeHeaderTitle(msg){
this.changeTitle(msg)
}
}
}
</script>
方法二:ref
父组件【./App.vue】
通过绑定 ref
标签属性,再使用 this.$refs
直接调用子组件实例对象的 $on
方法,完成子组件方法的绑定(
注意:数据在哪个组件里,方法就在哪个组件里)。
<template>
<div id="app">
<DemoHeader :title="title" ref="demoHeader"/>
</div>
</template>
<script>
import DemoHeader from './components/DemoHeader.vue'
export default {
name: 'App',
components: {
DemoHeader,
},
data(){
return {
title: "Demo-Title"
}
},
mounted(){
this.$refs.demoHeader.$on('changeTitle', (msg)=>{
this.title = msg
})
}
}
</script>
子组件【./components/DemoHeader.vue】
子组件通过 this.$emit
方法,触发在父组件中 间接注册
的方法(直接绑定在子组件实例上),传入参数完成子组件对父组件的数据传递。
<template>
<div>
<h1>{{ title }}</h1>
<button @click="changeHeaderTitle('你好,父组件!')">Change Title</button>
</div>
</template>
<script>
export default {
name: 'DemoHeader',
props: ['title'],
methods: {
changeHeaderTitle(msg){
this.$emit('changeTitle', msg)
}
}
}
</script>
方法三:作用域插槽
子组件【./components/DemoHeader.vue】
<template>
<div>
<slot name="demo" :students="students">
<h3>插槽默认元素</h3>
</slot>
</div>
</template>
<script>
export default {
name: 'DemoHeader',
data() {
return {
students: [
{
id: "001",
name: "小明",
age: 111,
},
{
id: "002",
name: "张三",
age: 11,
},
]
}
},
}
</script>
父组件【./App.vue】
通过 slot-scope
或者 scope
为作用域命名。比如此处的 datas
。
如果只需要 students
变量,则可以简写成 slot-scope="{ students }"
。
<template>
<div id="app">
<DemoHeader>
<template slot-scope="datas" slot="demo">
<div>
<span v-for="stu in datas.students" :key="stu.id">
【{{ stu.name }}/{{stu.age}}】<br>
</span>
</div>
</template>
</DemoHeader>
</div>
</template>
<script>
import DemoHeader from './components/DemoHeader.vue'
export default {
name: 'App',
components: {
DemoHeader,
},
}
</script>
方法四(任意传):原型链
原理:组件实例 VueComponent
原型链在指向 Object
之前会先指向 Vue
实例对象,因此绑定在 Vue
实例对象上的属性,
均能够被生命周期内的所有的 VueComponent
获取到。
入口函数【./main.js】
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
// 方法一:不通过 `beforeCreate` 绑定 `Vue` 实例对象,而是构造一个中间的 `VueComponent` 实例对象。
// const bus = Vue.extend({})
// Vue.prototype.$bus = new bus()
new Vue({
render: h => h(App),
// 方法二:直接绑定 `Vue` 实例对象
beforeCreate(){
Vue.prototype.$bus = this
}
}).$mount('#app')
父组件【./App.vue】
通过调用 Vue
实例对象上的 $on
完成事件的绑定。
<template>
<div id="app">
<DemoHeader :title="title"/>
</div>
</template>
<script>
import DemoHeader from './components/DemoHeader.vue'
export default {
name: 'App',
components: {
DemoHeader,
},
data(){
return {
title: "Demo-Title"
}
},
mounted(){
this.$bus.$on('changeTitle', (msg)=>{
this.title = msg
})
}
}
</script>
子组件【./components/DemoHeader.vue】
通过调用 Vue
实例对象上的 $emit
完成数据的传递与方法的调用。
注意:务必在子组件销毁之前,调用 $off
解绑具体的方法。
<template>
<div>
<h1>{{ title }}</h1>
<button @click="changeHeaderTitle('你好,父组件!')">Change Title</button>
</div>
</template>
<script>
export default {
name: 'DemoHeader',
props: ['title'],
methods: {
changeHeaderTitle(msg){
this.$bus.$emit('changeTitle', msg)
this.$destroy()
}
},
beforeDestroy(){
this.$bus.$off('changeTitle')
}
}
</script>
方法五(任意传):订阅与发布
首先,安装自己需要的消息订阅与发布的三方库,此处以 pubsub-js
为例。
安装:npm i pubsub-js
其他的参考 原型链
方式,使用几乎一模一样。
父组件(订阅)【./App.vue】
<template>
<div id="app">
<DemoHeader :title="title"/>
</div>
</template>
<script>
import DemoHeader from './components/DemoHeader.vue'
import pubsub from 'pubsub-js'
export default {
name: 'App',
components: {
DemoHeader,
},
data(){
return {
title: "Demo-Title"
}
},
mounted(){
this.pubId = pubsub.subscribe('changeTitle', (msgName, msg)=>{
this.title = msg
})
},
beforeDestroy(){
pubsub.unsubscribe(this.pubId)
}
}
</script>
子组件(发布)【./components/DemoHeader.vue】
<template>
<div>
<h1>{{ title }}</h1>
<button @click="changeHeaderTitle('你好,父组件!')">Change Title</button>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name: 'DemoHeader',
props: ['title'],
methods: {
changeHeaderTitle(msg){
pubsub.publish('changeTitle', msg)
}
},
}
</script>
方法六(任意传):Vuex
Vuex
有着特殊的应用场景,当不同的组件共用一些变量的时候(增、删、改、查),就考虑使用 Vuex
。
表单
【暂不整理】
样式
【暂不整理】
过渡与动画
三方库集成:npm i animate.css
简单动画
<!DOCTYPE html>
<html lang="en">
<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">
<title>Demo</title>
<style>
/* transition 无 name 属性,默认 .v-enter-active */
.demo-enter-active{
animation: demo 0.5s linear;
}
/* 默认 .v-leave-active */
.demo-leave-active{
animation: demo 1s linear reverse;
}
@keyframes demo {
from{
transform: translateX(-100%);
}
to{
transform: translateX(0px);
}
}
</style>
</head>
<body>
<div id="app">
<button @click="clickShow">{{ btn_name }}</button>
<transition name='demo' :appear="true">
<h1 v-show="isShow">动画演示</h1>
</transition>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
isShow: true,
btn_name: '隐藏'
},
methods: {
clickShow(){
this.isShow = !this.isShow
if (this.isShow){
this.btn_name = '隐藏'
}else{
this.btn_name = '显示'
}
}
}
})
</script>
</body>
</html>
UUID
nanoid
import {nanoid} from 'nanoid'
// 使用
uuid = nanoid()
DOM相关
DOM更新之后执行事件
...
this.$nextTick(function(){
// DOM刷新之后执行的操作
})
...
浏览器本地存储
持久性【localStorage】
// 存值
localStorage.setItem('key1', 'value') # 存字符串
localStorage.setItem('key2', JSON.stringify({'name':'kity'})) # 存JSON对象
// 取值(取一个非法的键值时,返回 null )
const value = localStorage.getItem('key2')
console.log(JSON.parse(value)) # 解析对象
// 删除
localStorage.removeItem('key1')
// 清空
localStorage.clear()
一次性【sessionStorage】
API 和 localStorage
一模一样,不同的是 sessionStorage
只在会话期间临时存储,关闭浏览器后存储内容自动删除。
异步请求
axios
npm i axios
<script>
import axios from 'axios'
export default {
methods: {
testApi(){
axios.get('url').then(
response => {
// 请求成功
// 取数据:response.data
},
error => {
// 请求失败
// 去错误信息:error.message
}
)
}
}
}
</script>
fetch
【暂不整理】
vue-cli常用配置
具体配置请移步官网教程:vue-cli
在 package.json
同级的目录下新增文件 vue.config.js
。
关闭语法检查
vue.config.js
文件中新增如下内容:
module.exports = {
// 关闭语法检查
lintOnSave: false
}
跨域问题
配置单代理模式
vue.config.js
文件中新增如下内容:
单代理模式,不支持多代理模式(且控制不了代理的路径)
module.exports = {
// 开启代理服务器
// 假设 axios 所在服务器端口为 8080,接口所在的端口为 4000
// 则此处需要配置 4000 的代理端口
// 请求接口由原来的 http://localhost:4000/xxx
// 改为 http://localhost:8080/xxx
devServer: {
proxy: 'http://localhost:4000'
}
}
配置多代理模式
添加请求前缀的方式,详细区分 本域请求 还是 跨域请求。
module.exports = {
devServer: {
proxy: {
'/api': {
// 原来的 http://localhost:4000/xxx
// 改为 http://localhost:8080/api/xxx
target: 'http://localhost:4000',
pathRewrite: {'^/api':''}, // 路由中忽略开头的 'api' 中间路径。
ws: true, // 用于支持 WebSocket。默认 true
changeOrigin: true // 为true时,伪造端口,与接口端口一致。默认 true
},
'/foo': {
target: '<other_url>'
}
}
}
}