Vue知识点总结
Vue
一、概述
1.1 vue介绍
vue是一种构建用户界面的渐进式框架。
1.2 vue特点
响应式
当在浏览器中修改对应变量的值,能够即时渲染到页面上生效。
1.3 npm相关命令
npm init -y # 管理工作目录
npm list [-g] --depth 0 # 查看安装好的npm包
npm install xxx@1.0 [-g] # 安装某个版本的包,本地安装则不添加-g
npm uninstall xxx [-g] # 卸载npm包,卸载有问题可以直接去对应的模块目录中删除
1.4 vue-cli相关命令
# 2版本的vue-cli
npm install vue-cli # 安装vue-cli脚手架工具
vue init webpack xxx # 初始化vue目录
cd xxx # 进入vue工作目录
npm run dev # 启动dev环境
npm run build
1.5 ES6常用语法
- 变量提升(let关键字、块级作用域、const常量)
- 模板字符串(
${}
) - 数据解构
- 类的定义
- 箭头函数(箭头函数的this是上下文,普通函数的this是最近的调用者)
二、Vue操作
2.1 vue实例
let vm = new Vue({
el: "选择器",
data: {} // 数据对象
})
备注:vue的响应式要求,响应的数据属性必须在实例创建之前已经定义在vue中,新增的vue数据属性不会触发响应式动作。
2.2 常用指令
2.2.1 v-text和v-html
用来展示Html代码
<div id="app">
<!-- 这个和v-text="msg"作用相同 -->
<div>{{hello}}</div>
<!-- 展示html代码需要用v-htmnl来表示 -->
<div v-html="mySpan"></div>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
hello: "helloworld",
mySpan: "<span style='color: red;'>red helloworld</span>"
}
})
</script>
2.2.2 v-bind
用来给元素绑定属性
<div id="app">
<div v-bind:title="title">{{hello}}</div>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
hello: "悬停鼠标",
// 实际上就是 <div title="xxx"></div>
title: "页面加载于" + new Date().toLocaleString()
}
})
</script>
2.2.3 v-show和v-on
v-show用来控制css的display属性,变化时不需要重新渲染页面;
v-on用来给标签绑定事件;
v-on相关的装饰器
.stop - 调用 event.stopPropagation()。
.prevent - 调用 event.preventDefault()。
.capture - 添加事件侦听器时使用 capture 模式。
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
.native - 监听组件根元素的原生事件。
.once - 只触发一次回调。
.left - (2.2.0) 只当点击鼠标左键时触发。
.right - (2.2.0) 只当点击鼠标右键时触发。
.middle - (2.2.0) 只当点击鼠标中键时触发。
.passive - (2.3.0) 以 { passive: true } 模式添加侦听器
<div id="app">
<h2 v-show="flag">helloworld</h2>
<button v-on:click="myClick">button</button>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
flag: true
},
methods: {
myClick: function (event) {
this.flag = ! this.flag
}
}
})
</script>
2.2.4 v-if和v-else
通过v-if来操作的dom元素会重新渲染,注意与show区分
<div id="app">
<h2 v-if="flag">yes</h2>
<h2 v-else="flag">no</h2>
<button v-on:click="myClick">button</button>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
flag: true
},
methods: {
myClick: function (event) {
this.flag = ! this.flag
}
}
})
</script>
2.2.5 v-for
用于循环处理数据
<div id="app">
<h2 v-for="item in items">
{{item.name}}
</h2>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
items: [
{name: "shenzhen", size: "small"},
{name: "beijing", size: "big"}
]
},
})
</script>
2.2.6 v-model
用于给表单控件做数据双向绑定
<div id="app">
<input type="text" v-model="value">
<h4>文本框: {{value}}</h4>
<input type="checkbox" value="shenzhen" v-model="city" id="shenzhen">
<label for="shenzhen">深圳</label>
<input type="checkbox" value="beijing" v-model="city" id="beijing">
<label for="beijing">北京</label>
<input type="checkbox" value="shanghai" v-model="city" id="shanghai">
<label for="shanghai">上海</label>
<h4>复选框: {{city}}</h4>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
value: "",
city: []
}
})
</script>
2.3 计算属性
计算属性返回的是函数。对于复杂的计算逻辑,应该使用计算属性。计算属性的数据是放入缓存的,当数据改变时会重新计算。
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
<script>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
</script>
2.4 数据监听watch
数据监听watch主要是用来监听数据改变的,可以观测到新旧值的改变,从而做出一些处理。但是这里有个Bug,数据监听的对象如果是值类型数据,那么可以正确判断出数据发生改变。而对于像列表对象,如果索引值发生变化,是没法感知到的。
// 数据监听的一个Bug,本质是由于数组是个对象,值改变但是地址没有变化,没有监听到
new Vue({
el: "#app",
data: {
name: "zimsky",
hobby: ["1", "2"]
},
methods: {
myClick: function(){
// this.name = "zimskyzeng"
// this.hobby[0] = "2" 这里的改变索引值不会生效
app.$set(this.hobby, 0, "2") // 需要用手动刷新的方式能够生效,显示刷新数据
}
},
watch: {
name: {
handler: function(val, oldVal) {
console.log(val)
console.log(oldVal)
},
deep: true //深度监听也没法生效
}
}
})
2.5 根实例属性获取
在vue中的根实例属性,都可以通过this.$
方式来获取,例如this.$el
,this.$data
等。
三、组件
3.1 全局组件注册
<!-- 写法1 使用这种写法会将app标签替换成<div><span>{{msg}}</span></div>-->
<!-- 组件可以接受的选项除了没有el外,和Vue实例基本一致,有data computed watch methods等-->
<div id="app"></div>
<script>
Vue.component("global-component", { // 没有el标签
template: `<div><span>{{msg}}</span></div>`,
data() { // 这里的data必须是函数
return {
msg: "helloworld",
}
}
});
let vm = new Vue({
el: "#app",
template: `<global-component></global-component>`
});
<!-- 写法2 使用这种写法会保留id="app"标签,在内部添加component内容 -->
<div id="app">
<global-component></global-component>
</div>
<script>
Vue.component("global-component", {
template: `<div><span>{{msg}}</span></div>`,
data() {
return {
msg: "helloworld",
}
}
});
let vm = new Vue({
el: "#app",
});
3.2局部组件注册
<div id="app"></div>
<script>
let localComponent = { // 写法上是注册全局组件的对象
template: `<div>{{ greeting }}</div>`,
data(){
return {
greeting: "helloworld"
}
}
};
let anotherComponent = {
template: `<div>{{ a }}</div>`,
data(){
return {
a: "aaa"
}
}
};
let vm = new Vue({
el: "#app",
template: `<div><my-component></my-component><another-component></another-component></div>`, // 这里写了template意味着app的div被替换掉
components: {
"my-component": localComponent,
"another-component": anotherComponent,
}
})
</script>
局部组件嵌套时的关系,如下代码
<div id="app"></div>
<script>
let localComponent = {
template: `<div>{{ greeting }}</div>`,
data(){
return {
greeting: "helloworld"
}
}
};
let App = {
template: `<localComponent></localComponent>`, // 这里的localComponent在下面的对象中注册
components: {
"localComponent": localComponent
}
};
let vm = new Vue({ // vue实例是执行入口,资源开始会从这里找
el: "#app",
template: `<App></App>`, // 这里的App在下面的对象中注册,键值相同仅需写值
components: {
App,
}
})
组件执行过程以及显式标签写法如下
<div id="app">
<App></App> <!-- vue执行到这里时,会去vue实例中寻找组件 -->
</div>
<script>
let localComponent = {
template: `<div>{{ greeting }}</div>`,
data(){
return {
greeting: "helloworld"
}
}
};
let App = {
template: `<localComponent></localComponent>`,
components: {
"localComponent": localComponent
}
};
let vm = new Vue({
el: "#app",
// template: `<App></App>`, 注释掉这行,则需要显式在html中写组件标签
components: {
App, // 标签名为App
}
})
</script>
3.3 父子组件之间传递消息
<div id="app">
<App></App>
</div>
<script>
let localComponent = { // 子组件
template: `<div>{{ greeting }} {{myAttr}}</div>`,
data(){
return {
greeting: "helloworld"
}
},
props: ["myAttr",] // 从这里获取父组件的属性
};
let App = { // 父组件
template: `<localComponent v-bind:myAttr="attr"></localComponent>`, // 传递方式为绑定自定义属性,attr对应data中的变量名
components: {
"localComponent": localComponent
},
data(){
return {
attr: "zimsky" // attr对应变量名
}
}
};
let vm = new Vue({
el: "#app",
// template: `<App></App>`,
components: {
App,
}
})
</script>
3.4 子父通信
<script>
let localComponent = {
template: `
<div>
<button @click="myTicket">按钮</button>
</div>`,
data() {
return {
greeting: "helloworld",
}
},
methods: {
myTicket: function (event) {
// 使用 this.$emit来向父组件提交事件
this.$emit("doTicket", "myTicket");
}
}
};
let App = {
// 父组件监听子组件事件
template: `
<div><h1>{{title}}</h1>
<localComponent @doTicket="doTicket"></localComponent>
</div>`,
components: {
localComponent
},
data() {
return {
title: "title",
}
},
methods: {
doTicket: function (val) {
this.title = val
}
}
}
var vm = new Vue({
el: "#app",
components: {
App,
}
})
</script>
3.5 非父子通信
let mid = new Vue() // 中间调度器
let component1 = {
template: `<div>{{message}}<button @click="clickMe">change</button></div>`,
data(){
return {
message: "com1"
}
},
methods: {
clickMe: function () {
mid.$emit("changeData", this.message)
}
}
}
let component2 = {
template: `<div>{{message}}</div>`,
data(){
return {
message: "com2"
}
},
mounted(){
mid.$on("changeData", data => {this.message = data})
}
}
let vm = new Vue({
el: "#app",
data: {
name: "shenzhen"
},
template: `<div><component1></component1><component2></component2></div>`,
components: {
component1,
component2
},
mounted: function () {
console.log("name", this.name)
console.log("el", this.el)
}
})
3.6 组件共享方法
(对于methods可以重复使用)
<div id="app">
<my-header></my-header>
</div>
<script>
let mixs = {
methods: {
mouthEnter: function () {
console.log("鼠标移入");
},
mouthLeave: function () {
console.log("鼠标移出");
}
}
};
let Header = {
template: `
<div><button v-on:mouseenter="mouthEnter">鼠标移入</button>
<button v-on:mouseleave="mouthLeave">鼠标移出</button></div>
`,
mixins: [mixs,]
};
let vm = new Vue({
el: "#app",
components: {
"my-header": Header,
}
})
</script>
3.7 设置组件插槽
用于接收标签体中的数据
<div id="app">
<my-header>a</my-header> <!-- 数据为a -->
<my-header>b</my-header>
<my-header>c</my-header>
</div>
<script>
let Header = {
template: `
<div>
<slot></slot> // 这里设置组件的插槽,用于接收组件标签体中的数据
</div>
`
};
let vm = new Vue({
el: "#app",
components: {
"my-header": Header,
}
})
</script>
四、Vue生命周期
4.1 beforeCreate
在实例初始化之后,此时数据data、事件methods、el标签都还没有。
4.2 created
Vue实例创建完成后调用,此时数据data、事件methods都已经有了,但是this.$el
还没有。
4.3 beforeMount
在挂载之前调用,此时this.$el
已经有了,但是数据仍然以{{}}
的形式预埋在标签中,还未渲染。
4.4 mounted
此时数据data、事件methods、this.$el
都已经有了,并且已经渲染并展示完成。
4.5 beforeUpdate
数据更新时调用。
4.6 updated
数据更新完成时调用。
4.7 activated
该钩子函数所在的组件,必须在keep-alive树中。keep-alive可以保存当前组件的状态(某个标签是否被点开,内容展开等),在下次激活时恢复该状态。activated用于被keep-alive缓存的组件激活时调用。
4.8 deactivated
该钩子函数所在的组件,必须在keep-alive树中。deactivated用于被keep-alive缓存的组件停用时调用。
4.9 补充知识点
Flex盒子模型
// 使盒子内部(例如ul)的对象都垂直水平居中展示
.el-menu {
display: flex;
align-items: center;
justify-content: center;
}
五、路由系统
5.1 路由系统的实现原理
<script>
// 表示当窗口发生变化时触发,URL中#后面的部分变化
window.onhashchange = function () {
switch (location.hash) { //变化成的内容
case '#/login':
document.getElementById('app').innerText='Login';
break;
case '#/home':
document.getElementById('app').innerText='Home';
break;
default:
document.getElementById('app').innerText='Default';
break;
}
}
</script>
5.2 路由系统基本用法
<script>
// 1 声明使用VueRouter
// 2 定义组件
// 3 将组件与VueRouter关联
// 4 将VueRouter注册到Vue实例中
// 在Vue根实例中使用VueRouter
Vue.use(VueRouter);
// 配置了3个组件
let Home = {
template: `<div>Home</div>`
};
let Login = {
template: `<div>Login</div>`
};
let Register = {
template: `<div>Register</div>`
};
// 将VueRouter和组件关联起来
let router = new VueRouter({
routes: [
{
name: 'home', // 用来配置命名路由
path: '/',
component: Home
},
{
name: 'login',
path: '/login',
component: Login
},
{
name: 'register',
path: '/register',
component: Register
},
]
});
// 将VueRouter注册到Vue标签中
let vm = new Vue({
router: router,
el: "#app",
// router-link会渲染成a标签,to变成href属性
// router-view是组件到渲染出口
// 注意使用命名路由时,to前面有冒号
// 下面注释部分时
template: `
<div>
<router-link :to="{name: 'home'}">Home</router-link>
<router-link :to="{name: 'login'}">Login</router-link>
<router-link :to="{name: 'register'}">Register</router-link>
<!--<router-link to="/">Home</router-link>-->
<!--<router-link to="/login">Login</router-link>-->
<!--<router-link to="/register">Register</router-link>-->
<router-view></router-view>
</div>
`
});
</script>
5.3 路由系统带参数
使用路由系统时,会给路由附带相关参数,通过如下方式实现。
<div id="app"></div>
<script>
Vue.use(VueRouter);
let Home = {
template: `
<div>
<h2>Home Page</h2>
</div>
`
};
let User1 = {
template: `
<div>
<h2>User1</h2>
</div>
`
};
let User2 = {
template: `
<div>
<h2>User2</h2>
</div>
`
};
let App = {
// 参数的值从data中获取,不需要写{{}},直接引用即可
template: `
<div>
<router-link :to="{name: 'home'}">Home</router-link>
<router-link :to="{name: 'user1', params: {uid: uid1 }}">User1</router-link>
<router-link :to="{name: 'user2', query: {uid: uid2 }}">User2</router-link>
<router-view></router-view>
</div>
`,
data() {
return {
uid1: 13,
uid2: 15
}
}
};
routes = [
{name: "user1", path: "/user1/:uid", component: User1}, // /user1/1
{name: "user2", path: "/user2", component: User2}, // /user2/uid=2
{name: "home", path: "/", component: Home}
];
let router = new VueRouter({
routes: routes
});
let vm = new Vue({
el: "#app",
template: `<App></App>`,
components: {
App: App
},
router: router
})
</script>
5.4 路由系统之子路由
<div id="app"></div>
<script>
Vue.use(VueRouter);、
let Home = {
template: `
<div>
<h3>主目录</h3>
<router-link :to="{name: 'child1'}">子目录1</router-link>
<router-link :to="{name: 'child2'}">子目录2</router-link>
<router-view></router-view>
</div>
`
};
let Child1 = {
template: `
<div>
<h4>子目录1</h4>
</div>
`
};
let Child2 = {
template: `
<div>
<h4>子目录2</h4>
</div>
`
};
let router = new VueRouter({
routes: [
{
// 能写都尽量写成name的形式
name: "home", path: "/home", component: Home, children: [
{name: "child1", path: "child1", component: Child1},
{name: "child2", path: "child2", component: Child2}
]
},
]
});
let App = {
template: `
<div>
<router-link :to="{name: 'home'}">主目录</router-link>
<router-view></router-view>
</div>
`
};
let vm = new Vue({
el: "#app",
router: router,
template: `
<App></App>
`,
components: {
App: App,
}
})
</script>
5.5 子路由+重定向
<div id="app"></div>
<script>
Vue.use(VueRouter);
let Home = {
template: `
<div>
<router-link :to="{name: 'login'}">登陆</router-link>
<router-link :to="{name: 'go'}">购物</router-link>
<router-link :to="{name: 'pay'}">支付</router-link>
<router-view></router-view>
</div>
`
};
let Login = {
template: `
<div>
<h4>登陆</h4>
</div>
`
};
let Go = {
template: `
<div>
<h4>购物</h4>
</div>
`
};
let Pay = {
template: `
<div>
<h4>支付</h4>
</div>
`
};
let App = {
template: `
<div>
<router-link :to="{name: 'home'}">首页</router-link>
<router-view></router-view>
</div>
`
};
let router = new VueRouter({
// 这里的history用于将URL中的#去掉
mode: "history",
routes: [
{
name: "home", path: "/", component: Home, children: [
{name: "login", path: "login", component: Login},
{name: "go", path: "go", component: Go},
// 在这里配置重定向,实现跳转
// 也可以使用命名路由 redirect: {name: "xx", params: {key: value}}
{name: "pay", path: "pay", component: Pay, redirect: "login"},
]
}
]
});
let vm = new Vue({
el: "#app",
template: `
<App></App>
`,
router: router,
components: {
App: App
}
})
</script>
5.6 路由系统之钩子函数
// 其他部分可以参考5.5代码,仅变更如下部分
let router = new VueRouter({
mode: "history",
routes: [
{
name: "home", path: "/", component: Home, children: [
{name: "login", path: "login", component: Login},
{name: "go", path: "go", component: Go},
// 在这里设置了钩子函数,用于校验是否需要登录状态
{name: "pay", path: "pay", component: Pay, meta: {require_login: false}},
]
}
]
});
// 路由钩子函数,to表示计划去哪个页面,from表示当前页面,next表示实际跳转的页面URL
router.beforeEach(function (to, from, next) {
console.log(to, from, next);
if (to.meta.require_login) {
// 如果是子路由的话,设置了name那么只需要写name对应的名称
next("login")
} else {
next()
}
});
5.7 手动路由
this.$router.push("/")
5.8 路由中的注意点
this.$route
表示存放路由所有信息的对象
this.$router
表示VueRouter的实例化对象
六、Vuex
主要用来存放组件间通信的数据,可以看成是一个仓库
6.1 安装vuex
进入vue工作目录,执行 "npm install vuex"本地安装,查看package.json确认安装成功。
6.2 vuex的简单用法
// 在main.js中引入vuex,这里没有采用解耦的方式
Vue.use(Vuex)
// 实例化一个Vuex.Store对象,如果是解耦的话,"export default new Vuex.Store({})"
// 写在src目录下新建一个目录
let store = new Vuex.Store({
// 调用方式: this.$store.state.name
// 这里的 this.$store this为Vue实例,$store是 Vue实例的属性
state: {
name: "zimsky"
},
// getters 用来做数据的过滤与处理
getters: {
new_name: function (state) {
return state.name + "- shenzhen"
},
// 这里仍然需要传入state,因为getters.new_name中用到了
another_name: function (state, getters) {
return getters.new_name + "- China"
}
},
// 用mutations来处理组件发送的事件
mutations: {
// 这里用到了state的数据,所以要用state
change_data: function (state, data) {
state.name = data
}
}
})
let vm = new Vue({
el: "#app",
store,
...
})
// Home.vue组件中使用Vuex的数据
<template>
<div>
<h2>这是首页</h2>
<h4>{{new_name}}</h4>
<h2>{{name1}}</h2>
<button v-on:click="myClick">点击更改name1数据</button>
</div>
</template>
<script>
export default {
name: "Home",
data() {
return {
// 这里的 name1 无法实时渲染数据,因为首次计算后获取到值,就不会再次使用this.$store.state.name这个函数来计算重新获取
// name1: this.$store.state.name,
new_name: this.$store.getters.another_name
}
},
methods: {
myClick: function (event) {
// 向vuex中提交事件,第一个参数是事件函数,后面的参数是data值
this.$store.commit("change_data", "Zimskyzeng")
}
},
computed: {
// 重点: 这里使用computed属性数据,能够根据 $store 中的数据变化实时更新 name1 的值
// computed核心: 数据的即时计算,如果这里的 name1 放在data属性中,则无法实时渲染新数据
name1: function () {
return this.$store.state.name
}
}
}
</script>
七、axios
这个axios是Vue中用来发送ajax请求的包
7.1 安装axios
npm install axios
7.2 axios的简单用法
// 在main.js中注册axios
// 注意: 由于axios不是Vue内置的,需要使用原型链来注册到Vue对象中
import axios from 'axios'
Vue.prototype.$axios = axios
<!-- 在组件中使用axios发送ajax请求 -->
<script>
export default {
name: "Home",
mounted(){
// 在这里使用 request 方法发送ajax请求
this.$axios.request({
url: 'http://www.baidu.com',
method: 'get'
// then 是请求成功后调用的方法
}).then(function (data) {
console.log(data)
// catch 是请求失败后调用的方法
}).catch(function (data) {
})
}
}
</script>
八、npm
本质上就是包管理工具,常用命令如下:
1.npm init -y 将当前目录被npm管理;
2.npm install PACKAGE 将包安装到当前目录下,如果加上-g则是装到全局,如果加版本号为package@1.1.1;
3.npm uninstall 卸载包;
webpack是用来打包的,将src目录下面的js打包成适合浏览器识别的语句(某些语句浏览器不支持)。
vue-cli自带了webpack
Vue注意总结
- JS对象名都不用加引号;
- 在实例化Vue的时候,components是复数形式;
- 实例化router时,里面的routes是复数形式;
- Vue实例中的data用的是对象来表示,组件的data是用函数来表示