Vue 作者 刘雨溪
vue是一个前端渐进式框架 采用mvvm模式 m-module v-view vm-viewModule 可以实现mvc controller的大部分功能 。 相当于 controller 是一个公司老板 viewModule 是一个秘书
mvc :m-module v-view c-controller
在mvc模式中 module 和 view都会将逻辑交给 controller 去处理 使用服务器会承担很大的压力
但是在mvvm模式中 vm 相当于把model和view的相关文件或逻辑处理然后把角色的部分交给controller
Vue 使用
<div id="app"></div>
// vue是一个构造函数 使用的时候需要实例化的vue对象
new Vue({
el:"#app",//vue挂载点 所有的vue代码都包含在内
//data 存储数据状态
data:{
name:"安琪阿脸"
}
})//.$mount('#app') 另一种挂载方式
Vue指令
<!-- {{}} 用来渲染vue的数据状态 里面可以进行简单的逻辑运算(也可调用函数) -->
v-text
<!-- v-text相当于在dom中插入对应的内容 会把变量的值当成字符串进行解析 不会解析标签 -->
v-show v-if
<!-- v-show 和 v-if都可以控制元素的显示和隐藏
区别
v-show 通过css样式控制元素的显示和隐藏
v-if 通过删除和新增dom控制元素的显示和隐藏
当v-show的值是true的时候相当与删除了样式display: non;v-show的值是false的时候相当于跟当前元素添加样式display: none
v-if 的值是true的时候相当于新增当前dom;v-if的只是false的时候相当于删除了当前dom
不能在v-if和v-else之间添加逻辑 否则会报错
-->
v-bind
v-bind是一个属性绑定指令 指令可以绑定dom中所有的属性 v-bind 可以缩写为 :
v-model
v-model是数据双向绑定的指令 也是vue中的一个核心功能
在input中相当于一个v-bind和一个v-on:input
在select中相当于v-bind 和 v-on:chang
原理: vue3: proxy
vue2:一方面modal层通过defineProperty来劫持每个属性,一旦监听到变化通过相关的页面元素更新。另一方面通过编译模板文件,为控件的v-model绑定input事件,从而页面输入能实时更新相关data属性值
v-for
v-for 是一个循环指令
当v-for循环数组的时候 可以有两个参数 第一个是数组项 第二个是数组的下标
当循环对象的时候第一个参数是对象键值对中的值(value) 第二个参数是键值对中的键(key) 第三个是索引值
v-pre [c-cloak]
v-pre会跳过编译阶段 会把 {{}} 直接显示在页面上
[v-cloak] 会跳过 {{}} 编译阶段 直接显示完整的实例 要配合 style [cloak] { display : none;} 使用
组件
//组件其实就相当于一个自定义的标签
// 组件注册是名字可以使用驼峰可以使用连接符但是使用组件的时候标签不能写成驼峰
// 标签写的时候可以不成对 但是建议写成标签对
// 封装的组件可以多次使用
//这是全局注册组件的方法 第一个是组件的名称 第二个是组件的模板
// 全局注册的组件在任何地方都可以直接使用 而局部注册的组件只可以在注册组件的内部使用
<div id="app">
<child></child>
</div>
<script src="../js/vue.js"></script>
<script>
const child = {
template:`
<div>组件1
<child2></child2>
</div>
`
}
const child2= {
template:`
<div>组件2</div>
`
}
//组件其实就相当于一个自定义的标签
// 组件注册是名字可以使用驼峰可以使用连接符但是使用组件的时候标签不能写成驼峰
// 标签写的时候可以不成对 但是建议写成标签对
// 封装的组件可以多次使用
//这是全局注册组件的方法 第一个是组件的名称 第二个是组件的模板
// 全局注册的组件在任何地方都可以直接使用 而局部注册的组件只可以在注册组件的内部使用
Vue.component('child2',child2)
new Vue({
el:"#app",
// 组件局部注册的方法
components:{
child
}
})
</script>
组件嵌套
<div id="app">
{{game}}
<parent></parent>
</div>
<script src="../js/vue.js">
</script>
<script>
// 可以把组件写在另一个组件的需要的地方
// 组件嵌套是将子组件写在父组件的模板内部
// 而不是把组件写在父组件的自定义元素内部
// 如下 parent 为 child1 和child2 的父组件
// child1 和 child2 是parent的子组件
// child1 和 child2 是非父子组件
var parent = {
template: `
<div>父组件
<child1></child1>
</div>
`
}
var child1 = {
// 使用跟组件中的数据的时候报错提示game未定义
// 因为vue中非自己组件内的数据不能直接使用
template: `
<div>子1</div>
`
}
var child2 = {
template: `
<div>子2</div>
`
}
Vue.component('parent', parent)
Vue.component('child1', child1)
Vue.component('child2', child2)
new Vue({
el: "#app",
data: {
game: "忍者必须死"
}
})
</script>
组件传值
父子传值 1.props:[] 2 this.$parent 3 provide
<div id="app">
<!-- 可以通过自定义属性将父组件的数据绑定在要使用该数据的子组件标签上 -->
<parent content="123" title-text="去你的把" :pres="arr"></parent>
<!-- 不能驼峰命名自定义属性 因为识别不了 -->
<!-- <parent :content="content" titleText="去你的把"></parent> -->
</div>
<script src="../js/vue.js"></script>
<script>
var parent = {
// 子组件可以通过props接受子组件的属性从而拿到属性值
// 之后可以在页面中直接使用该属性来渲染属性值
// props的值可以是一个数组 它里面可以是该组件所有属性的集合
// 组件标签上绑定的属性是 title-text 接收属性的时候要改成驼峰命名 titleText
// 使用下划线和小写命名的属性可以随意
// props: ['content', 'titleText'],
// props也可以是一个对象在对象中接受自定义属性 可以规定数据的类型和默认值 定义数据是否是必须要传递的
props: { // 默认值是在传递的数据的值是undefined的时候会生效或者没有传递该属性的时候也会生效
content: {
type: String,
required: true, // props配置了必传 则在组件上面就必须有该属性
default: "进可孤身一挑五"
},
pres: {
type: Array,
default: () => { // props给数组和对象设置默认值的时候需要使用函数返回默认值
return []
}
}
},
inject: ['text'],
template: `
<div>{{content}}<child></child> {{pres}}</div>
`,
created() {
// 在子组件中使用this.$parent可以直接使用父组件的值
// 孙级组件可以通过this.$parent.$parent来使孙级组件使用爷级组件的值
// console.log(this.$parent.arr)
// console.log(this.text)
},
}
var child = {
inject: ['text'],
template: `
<div>子组件</div>
`,
created() {
// console.log(this.$parent.$parent.arr)
console.log(this.text)
},
}
Vue.component('parent', parent)
Vue.component('child', child)
new Vue({
el: "#app",
// provide开发组件库或者高阶组件的时候使用的方法
provide: {
text: "你们是麻瓜"
},
data: {
// content: []
content: undefined,
arr: ['特朗普', "奥巴马", "小布什", "老布什"]
}
})
</script>
子向父组件传值 1.事件映射 2.this.$children
<div id="app">
<parent @send:data="getData"></parent>
</div>
<script src="../js/vue.js"></script>
<script>
var parent = {
template: `
<div>
父组件
<button @click="fun">点击</button>
</div>
`,
data() { // 函数有作用域 使组件内的数据单独在组件内使用
return {
text: "我没讲过这个知识点"
}
},
methods: {
fun() {
// 事件映射其实相当于自定义了一个事件 第一个参数是自定义(映射)事件的名称 第二个参数是要携带的参数
// 事件映射在那个组件中定义就在当前组件定义的元素上监听该事件的触发
this.$emit('send:data', this.text)
}
}
}
var child = {
template: `
<div>子组件</div>
`
}
Vue.component('parent', parent)
Vue.component('child', child)
new Vue({
el: "#app",
created() {
},
mounted() {
// 通过this.$children也可以找到父组件中的数据
console.log(this.$children[0].text)
},
methods: {
getData(data) {
console.log(data)
}
}
})
</script>
sync
<div id="app">
<!-- .sync修饰符的作用 相当于在标签上添加了一个@update:变化的变量 然后把在事件传递过来的值直接赋值给我绑定的变量-->
<child :content.sync="content"></child>
</div>
<script src="../js/vue.js"></script>
<script>
var child = {
props: ['content'],
template: `
<div>
这是子组件.
<button @click="fun">点击</button>
</div>
`,
methods: {
fun() {
this.$emit('update:content', "你是个麻瓜")
}
}
}
Vue.component('child', child)
new Vue({
el: "#app",
data: {
content: "hello world"
},
watch: {
content(to) {
console.log(to)
}
}
})
</script>
非父子组件传值
<div id="app">
<com1></com1>
<com2></com2>
</div>
<script src="../js/vue.js"></script>
<script>
// js添加事件监听的方法 第一个参数是监听事件的事件类型 第二个参数是监听事件的回调函数
// 第三个参数是是否允许捕获
// document.addListener('click', function name(e) {
// }, true)
var com1 = {
template: `
<div>
父组件
<button @click="fun">点击</button>
</div>
`,
data() {
return {
text: "hello world"
}
},
methods: {
fun() {
this.bus.$emit('send:data', this.text)
}
},
}
var com2 = {
template: `
<div>父组件</div>
`,
data() {
return {
}
},
created() {
// console.log(this)
// var _that = this // 保存当前组件的this对象
// this.bus.$on("send:data", function (params) {
// console.log(_that)
// // console.log(params)
// })
this.bus.$on("send:data", (params) => {
console.log(this)
})
},
destroyed() {
this.bus.$off('send:data') //每次传输完销毁
},
}
Vue.prototype.bus = bus
new Vue({
el: "#app",
components: {
com1,
com2
}
})
</script>
插槽
匿名插槽与具名插槽
<div id="app">
<!-- 这里btn的内容会直接替换掉com组件内的slot -->
<!-- <btn>点击</btn>
<btn>确定</btn>
<btn>取消</btn>
<btn>编辑</btn> -->
<!-- 我们也可以用一组dom结构取替换solt -->
<com2>
<!-- 可以通过template标签和v-slot指令配置选中指定的插槽 -->
<template v-slot:default>
<ul>
<li>帅比你好</li>
</ul>
</template>
<!-- <template v-slot:slot2>
<ul>
<li><button>你是个麻瓜 帅什么帅</button></li>
</ul>
</template> -->
</com2>
</div>
<script src="../js/vue.js"></script>
<script>
var com = {
// 只写slot的插槽叫做匿名插槽
// 其实匿名插槽也是具名插槽不过匿名插槽的名字是default
template: `
<button>
<slot></slot>
</button>
`
}
var com2 = {
// 这种带有名字的插槽叫做具名插槽
template: `
<div>
<slot></slot>
这是com2
<slot name="slot2"></slot>
</div>
`
}
Vue.component('btn', com)
Vue.component('com2', com2)
new Vue({
el: "#app"
})
</script>
作用域插槽
<div id="app">
<com>
<!--
可以看到data是一个对象
当插槽是有name属性的时候 v-slot="data"拿不到数据
但是使用 v-slot:name="data"才可以取到数据
-->
<!-- <template v-slot:aaa="data">
<div>{{data}}</div>
</template> -->
<!-- 当插槽是用名字的时候写成v-slot="值"的时候取不到数据 -->
<template v-slot="data">
<!-- 可以看到data是一个对象 -->
<div>{{data}}</div>
</template>
</com>
<btn type="primary">点击</btn>
<btn type="danger">点击</btn>
</div>
<script src="../js/vue.js"></script>
<script>
const com = {
// template: `
// <div>
// lol
// <slot name="aaa" content="德玛西亚" text="hello world" a="明天不上课"></slot>
// </div>
// `
template: `
<div>
lol
<slot name="aaa" content="德玛西亚" text="hello world" a="明天不上课"></slot>
</div>
`
}
Vue.component('com', com)
new Vue({
el: "#app"
})
</script>
class绑定
<style>
html,body{
height: 100%;
margin: 0;
}
#app{
height: 100%;
}
ul{
height: 100%;
list-style: none;
padding: 0;
margin: 0;
display: flex;
justify-content: space-around;
align-items: center;
}
.container{
height:calc(100% - 40px)
}
.list_box{
height: 40px;
}
.active{
color: #ff6700;
}
.show{
font-size: 50px;
}
</style>
<body>
<div id="app">
<div class="container">
<!-- 如果绑定的class的值是对象的时候
键的值是true的时候 标签会有一个以键命名的类名 如果是false的时候 则没有 -->
<div :class="{active}">
你是个麻瓜
</div>
<!-- 也可以使用对象来完成 在data中填写他的键与属性 -->
<div :class="classObjet">你是个麻瓜</div>
</div>
<div class="list_box">
<ul>
<!-- 绑定class的值可以是一个数组 数组中是calss的类名的集合
绑定class并不影响正常class的使用 如果两者都使用 会把两个class的类名全部写入class类名
绑定class class的值是一个变量 也可以是一个简单的逻辑语句
绑定的clss值也可以是一个字符串拼接变量的形式 -->
<li :class="[current === 0?'active':''] " @click="changeActive(0)">首页</li>
<li :class="[current === 1?'active':''] " @click="changeActive(1)">分类</li>
<li :class="[current === 2?'active':''] " @click="changeActive(2)">星球</li>
<li :class="[current === 3?'active':''] " @click="changeActive(3)">购物车</li>
<li :class="[current === 4?'active':''] " @click="changeActive(4)">我的</li>
</ul>
</div>
</div>
<script>
var vm=new Vue({
el:'#app',
data:{
//记录点击的是第几个
current:0,
active:true,
classObjet: {
active: false,
show: true,
}
},
methods:{
changeActive(num){
this.current = num
}
}
});
</script>
style绑定
<div id="app">
<!-- 键值对 -->
<button style="color:green">点击</button>
<!-- 绑定style本身是要接收一个对象 -->
<button :style="styleObj">点击</button>
<button :style="{color:styleObj.color,fontSize:'20px'}">点击</button>
<button :style="{color:styleObj.color,fontSize:'20px'}">点击</button>
<button :style="[styleObj,back]">点击</button>
</div>
<script>
var vm=new Vue({
el:'#app',
data:{
styleObj:{
color:'red',
fontSize:'20px'
},
back:{background:'red'}
},
methods:{}
});
</script>
路由
认识路由
<div id="app">
<!-- 渲染路由组件的标签 -->
<router-view></router-view>
</div>
<script>
//路由组件不需要vue.component去注册
const com = {
template:`
<div>你是个麻瓜</div>
`
}
const com2 = {
template:`
<div>你才是个麻瓜</div>
`
}
// VueRouter是一个构造函数 使用的时候需要实例化对象
const router = new VueRouter({
// routes是路由对象的集合 路由对象包含了页面的路由相关的所有的信息
// 一个路由其实是一个页面的跳转链接
routes: [
// 当path的值和
// file:///E:/vue/%E8%AF%BE%E5%A0%82%E4%BB%A3%E7%A0%81/07router.html/071%E8%AE%A4%E8%AF%86vue%E8%B7%AF%E7%94%B1.html#/aaa/bbb
// 页面链接#后面的值一致时就显示当前路由对象的组件
{
path:'/', //页面链接的路径
component:com
},
{
path:'/a', //页面链接的路径
component:com2
}
]
})
var vm=new Vue({
el:'#app',
router,//将路由的实例化对象和vue实例化对象关联起来
data:{},
methods:{}
});
</script>
#### 路由分析
<body>
<div id="app">
<div class="container">
<router-view></router-view>
</div>
<ul class="nav_box">
<!--
router-link是路由跳转的标签 它默认会转化成a标签 可以通过tag属性自定以标签
to属性是跳转到的页面的路由对象或者path地址
-->
<!--
active-class
是路由匹配到了当前路由就会添加他对应的值到class中
他的默认值是router-link-active
exact-active-class 是路由精确匹配 只有当前路由地址跟path的值完全相等的时候会将他的值添加进入class中
他的默认值是router-link-exact-active
-->
<router-link tag="li" active-class="active" exact-active-class="isActive" to="/">首页</router-link>
<router-link tag="li" active-class="active" exact-active-class="isActive" to="/dicsover">发现</router-link>
<router-link tag="li" active-class="active" exact-active-class="isActive" to="/order">订单</router-link>
<router-link tag="li" active-class="active" exact-active-class="isActive" to="/profile">我的</router-link>
</ul>
</div>
<script src="../js/vue.js"></script>
<script src="../js/vue-router.js"></script>
<script>
let index = {
template:`
<div>首页</div>
`
}
let dicsover = {
template:`
<div>发现</div>
`
}
let order = {
template:`
<div>订单</div>
`
}
let profile = {
template:`
<div>我的</div>
`
}
const routes = [
{
path:'/',
name:'index',
component:index
},
{
path:'/dicsover',
name:'dicsover',
component:dicsover
},
{
path:'/order',
name:'order',
component:order
},
{
path:'/profile',
name:'profile',
component:profile
}
]
let router = new VueRouter({
routes
})
new Vue({
el:'#app',
router,
data:{},
methods:{}
});
</script>
动态组件
<style>
.tab_box {
height: 200px;
width: 300px;
margin: 0 auto;
border: 1px solid #333;
}
.tabs{
height: 30px;
width: 100%;
}
.content {
height: 170px;
border-top: 1px solid #333;
}
</style>
</head>
<body>
<div id="app">
<div class="tab_box">
<div class="tabs">
<button @click="changTab(1)">tab1</button>
<button @click="changTab(2)">tab2</button>
</div>
<div class="content">
<keep-alive>
<component :is="componentId"></component>
</keep-alive>
</div>
</div>
</div>
<script src="../js/vue.js"></script>
<script>
// 动态本身进行切换的时候会触发生命周期(钩子函数)
// 所以也就意味着组件会重新渲染
// 离开当前动态组件时组件会被销毁
const content1 = {
template: `
<div>tab1对应的内容</div>
`,
created() {
console.log('1')
},
destroyed() {
// console.log('销毁了')
},
}
const content2 = {
template: `
<div>tab2对应的内容</div>
`,
created() {
// console.log('2')
}
}
Vue.component('content1', content1)
Vue.component('content2', content2)
new Vue({
el: "#app",
data: {
componentId: 'content1'
},
methods: {
changTab(num) {
switch (num) {
case 1:
this.componentId = "content1"
break;
case 2:
this.componentId = "content2"
break;
default:
break;
}
}
}
})
</script>
</body>
过渡动画
网址 https://cn.vuejs.org/v2/guide/transitions.html
Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。包括以下工具:
在 CSS 过渡和动画中自动应用 class
可以配合使用第三方 CSS 动画库,如 Animate.css
在过渡钩子函数中使用 JavaScript 直接操作 DOM
可以配合使用第三方 JavaScript 动画库,如 Velocity.js
过渡的类名
在进入/离开的过渡中,会有 6 个 class 切换。
1.v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
2.v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
3.v-enter-to:2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
4.v-leave:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
5.v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
6.v-leave-to:2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。
对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>,则 v- 是这些类名的默认前缀。如果你使用了 <transition name="my-transition">,那么 v-enter 会替换为 my-transition-enter。