写在前面: 知识总结于:尚硅谷
1 Vue.js是什么?
1). 一位华裔前Google工程师(尤雨溪)开发的前端js库
2). 作用: 动态构建用户界面
3). 特点:
* 遵循MVVM模式
* 编码简洁, 体积小, 运行效率高, 移动/PC端开发
* 它本身只关注UI, 可以轻松引入vue插件和其它第三库开发项目
4). 与其它框架的关联:
* 借鉴angular的模板和数据绑定技术
* 借鉴react的组件化和虚拟DOM技术
5). vue包含一系列的扩展插件(库):
* vue-cli: vue脚手架
* vue-resource(axios): ajax请求
* vue-router: 路由
* vuex: 状态管理
* vue-lazyload: 图片懒加载
* vue-scroller: 页面滑动相关
* mint-ui: 基于vue的组件库(移动端)
* element-ui: 基于vue的组件库(PC端)
前端框架与库的区别?
- jquery 库 => DOM(操作DOM) + 请求
- 框架
- 全方位功能齐全
- 简易的DOM体验 + 发请求 + 模板引擎 + 路由功能
2 插值表达式
-
{{ 表达式 }}
- 对象 (不要连续3个{{ {name:'jack'} }})
- 字符串 {{ 'xxx' }}
- 判断后的布尔值 {{ true }}
- 三元表达式 {{ true?'是正确':'错误' }}
-
可以用于页面中简单粗暴的调试
-
要用插值表达式 必须要data中声明该属性
3 指令
v-text:元素的textContent属性,必须是双标签 跟{{ }}效果是一样的 使用较少
v-html: 元素的innerHTML
v-if v-else v-show: 显示/隐藏元素
* v-if : 判断是否插入这个元素,相当于对元素的销毁和创建, 相当于 appendChild() removeChild() 消耗dom。
* v-else : 与v-if一起使用, 如果value为false, 将当前标签输出到页面中
* v-show: 就会在标签中添加display**样式**, 如果value为true, 会给元素的style加上`dispaly:block`; 否则是`display:none`
-
v-bind
:属性名="变量名"
-
v-on
@原生事件名="定义的函数名"
-
v-for 优先级最高
-
遍历数组: 如果是数组没有id,
v-for="(item,index) in arr" :class="index" :key='index'
-
遍历对象 :
v-for="value in person" $key
-
v-model 双向绑定
<template>
<div>
<!-- 双向绑定-->
<input type="text" v-model="msg">
<input type="text" :value="msg" @input="msg=$event.target.value">
<!-- 单项绑定-->
<input type="text" :value="msg">
<br>
{{msg}}
</div>
</template>
<script>
export default {
name: "App",
data(){
return {
msg:'this is a test'
}
},
methods:{
inchange(event){
this.msg=event.target
console.log(event.target.value)
}
}
}
</script>
v-on
事件监听
事件的基本使用:
- 1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;
- 2.事件的回调需要配置在methods对象中,最终会在vm上;
- 3.methods中配置的函数,不要用箭头函数!否则this就不是vm了,而是window;
- 4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
- 5.@click="demo" 和 @click="demo($event)" 效果一致,但后者可以传参;
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>事件的基本使用</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- <button v-on:click="showInfo">点我提示信息</button> -->
<button @click="showInfo1">点我提示信息1(不传参)</button>
<button @click="showInfo2($event,66)">点我提示信息2(传参)</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
},
methods:{
showInfo1(event){
// console.log(event.target.innerText)
// console.log(this) //此处的this是vm
alert('同学你好!')
},
showInfo2(event,number){
console.log(event,number)
// console.log(event.target.innerText)
// console.log(this) //此处的this是vm
alert('同学你好!!')
}
}
})
</script>
</html>
事件修饰符
@click.prevent="showInfo"
Vue中的事件修饰符:
- 1.prevent:阻止默认事件(常用);
- 2.stop:阻止事件冒泡(常用);
- 3.once:事件只触发一次(常用);
- 4.capture:使用事件的捕获模式;
- 5.self:只有event.target是当前操作的元素时才触发事件;
- 6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
键盘事件
1.Vue中常用的按键别名:
- 回车 => enter
- 删除 => delete (捕获“删除”和“退格”键)
- 退出 => esc
- 空格 => space
- 换行 => tab (特殊,必须配合keydown去使用)
- 上 => up
- 下 => down
- 左 => left
- 右 => right
2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
3.系统修饰键(用法特殊):ctrl、alt、shift、meta
- (1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
- (2).配合keydown使用:正常触发事件。
4.也可以使用keyCode去指定具体的按键(不推荐)
5.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
4. Vue对象的选项
1). el
指定dom标签容器的选择器;
Vue就会管理对应的标签及其子标签
注意:由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue的实例了,而是window
el的两种写法
const v = new Vue({
el:'#root1',//第一种写法
data:{
name:'july'
}
})
console.log(v)
v.$mount('#root1')//第二种写法,挂载
2). data
对象或函数类型
指定初始化状态属性数据的对象
vm也会自动拥有data中所有属性
页面中可以直接访问使用
数据代理: 由vm对象来代理对data中所有属性的操作(读/写)
data的两种写法
new Vue({
el:'#root1',
//data第一种写法:对象式
data:{
name:'july'
},
//data第二种写法:函数式
data:function () {
console.log('@@@',this)
return{
name:'july'
}
}
3). render
最初的render:
render(createElement){
return createElement('h1','hello')
}
简化版:
render: q => q('h1','hello')
最终使用app main.js
import Vue from "vue";
import App from "./App";
Vue.config.productionTip = false
new Vue({
el: '#app',
render: h => h(App)
})
4). methods
包含多个方法的对象
供页面中的事件指令来绑定回调
回调函数默认有event参数, 但也可以指定自己的参数
所有的方法由vue对象来调用, 访问data中的属性直接使用this.xxx
5) computed
包含多个方法的对象
对状态属性进行计算返回一个新的数据, 供页面获取显示
一般情况下是相当于是一个只读的属性
利用set/get方法来实现属性数据的计算读取, 同时监视属性数据的变化
* 如何给对象定义get/set属性
* 在创建对象时指定: `get name () {return xxx} / set name (value) {}`
* 对象创建之后指定: `Object.defineProperty(obj, age, {get(){}, set(value){}})`
6) watch
包含多个属性监视的对象
分为一般监视和深度监视
xxx: function(value){}
xxx : {
deep : true,
handler : fun(value)
}
另一种添加监视方式: vm.$watch('xxx', function(value){})
4 Vue 组件化开发
5 组件通信
1. 父子组件通信
父子组件之间不能直接相互访问data内容
父组件 通过 props 访问子组件(三步):
1.App.vue(父组件)>>
<div class="main">
<div class="content">
<my-main msg="hello"></my-main><!-- 第一步: 在这里传入msg的值-->
</div>
2.myMain.vue(子组件)>>
<template>
{{msg}}<!--第三步.最后在这里可以展示msg-->
</template>
<script>
import myConn from "./childComp/myConn";
export default {
name: "myMain",
props:['msg'], //第二步.通过props 这里 接受 msg 的值
data(){
},
components:{
myConn
}
}
</script>
<style scoped>
</style>
下面是完整的
- 注 : 注意这里: :myTitle 父组件传入的一定是子组件中一模一样的名字,不可以:my-title App.vue >>
<template>
<section class="conn">
<header class="header">
<my-header></my-header>
</header>
<div class="main">
<div class="content">
<!-- 注意这里: :myTitle 父组件传入的一定是子组件中一模一样的名字,不可以:my-title-->
<my-main msg="hello"
:myTitle="msg"
:article="article"
></my-main><!-- 在这里传入msg的值-->
</div>
<div class="sidebar">
<my-sidebar></my-sidebar>
</div>
</div>
<footer class="footer"></footer>
</section>
</template>
<script>
import myHeader from "./components/myHeader";
import myMain from "./components/myMain";
import mySidebar from "./components/mySidebar";
import HelloVue from './components/HelloVue'
export default {
name: 'App',
data(){
return{
msg:'this is app data msg',
article:[
'11111111111','222222222222','33333333333'
]
}
},
components: {
myHeader,
myMain,
mySidebar,
// HelloVue
}
}
</script>
myMain.vue >>
<template>
<my-conn></my-conn>
<my-conn></my-conn>
{{msg}}<!--第三步.最后在这里可以展示msg-->
{{show()}}
<br>
{{myTitle}}
<br>
{{article}}
<span v-for="item in article">{{item}}</span>
</template>
<script>
import myConn from "./childComp/myConn";
export default {
name: "myMain",
//props:[name1,name2,...]默认是这种形式
props:{
msg:{
type:String,
default:'####'// 默认是'####'
},
myTitle:{
type:String
},
article:{
type:Array,
required:true// 表示该值是必须的,没有将
// 会报错
}}, //第二步.通过props 这里 接受 msg 的值
data(){
return{
msg1:'world'
}
},
components:{
myConn
},
methods:{
show(){
return this.msg + this.msg1
}
}
}
</script>
子组件 通过 $emit Events 访问父组件
注意:一层之间的父子传递,可以用$emit Events ; 如果是多层用vuex;
mycoun.vue >>
<template>
<div class="myConn">
<button @click="changeNum(2)">+</button>
{{mess}}
<br>
<span v-for="item in article">{{item}}</span>
</div>
</template>
<script>
export default {
name: "myConn",
data(){
return{
mess:'this is main test'
}
},
props:{
article: {
type:Array
}
},
methods:{
changeNum (num){
this.$emit('mycountevent',num)
}
}
}
</script>
myMain.vue >>
<template>
<div style="width: 100px; height: 50px;background-color: blanchedalmond">{{count}}</div>
<my-conn @mycountevent="mydemo"></my-conn>
<my-conn @mycountevent="mydemo" :article="article"></my-conn>
{{msg}}<!--第三步.最后在这里可以展示msg-->
{{show()}}
<br>
{{myTitle}}
<br>
{{article}}
<span v-for="item in article">{{item}}</span>
</template>
<script>
import myConn from "./childComp/myConn";
export default {
name: "myMain",
//props:[name1,name2,...]默认是这种形式
props:{
msg:{
type:String,
default:'####'// 默认是'####'
},
myTitle:{
type:String
},
article:{
type:Array,
required:true// 表示该值是必须的,没有将
// 会报错
}}, //第二步.通过props 这里 接受 msg 的值
data(){
return{
msg1:'world',
count:0
}
},
components:{
myConn
},
methods:{
show(){
return this.msg + this.msg1
},
mydemo(data){
this.count += data
}
}
}
</script>
<style scoped>
</style>
2. Vue父子组件之间的访问方式
子组件调用父组件的方法 $parent
or $root
myConn.vue >>
<template>
<div class="myConn">
<button @click="changeNum(2)">+</button>
{{mess}}
<button @click="one">++</button>
<br>
{{num}}
<br>
<span v-for="item in article">{{item}}</span>
</div>
</template>
<script>
export default {
name: "myConn",
data(){
return{
mess:'this is main test',
num:0
}
},
props:{
article: {
type:Array
}
},
methods:{
changeNum (num){
this.$emit('mycountevent',num)
},
one(){
console.log('子组件myConn中one()')
this.$parent.changen()
console.log( this.$parent.count)
this.$parent.$parent.appmet()
console.log( this.$parent.$parent.msg)
this.$root.appmet()
},
changeone(){
this.num++
}
}
}
</script>
<style scoped>
.myConn{
width: 90%;
height: 100px;
background-color: aquamarine;
margin: 10px;
}
</style>
父组件调用子组件的方法 $children
or $refs
mymain.vue >>
<template>
<div style="width: 100px; height: 50px;background-color: blanchedalmond">
{{count}}
<button @click="two">让子组件+1</button>
</div>
<my-conn ref="aaa" @mycountevent="mydemo"></my-conn><!-- ref="aaa" 相对于给这行组件起一个别名aaa -->
<my-conn ref="bbb" @mycountevent="mydemo" :article="article"></my-conn>
{{msg}}<!--第三步.最后在这里可以展示msg-->
{{show()}}
<br>
{{myTitle}}
<br>
{{article}}
<span v-for="item in article">{{item}}</span>
</template>
<script>
import myConn from "./childComp/myConn";
export default {
name: "myMain",
//props:[name1,name2,...]默认是这种形式
props:{
msg:{
type:String,
default:'####'// 默认是'####'
},
myTitle:{
type:String
},
article:{
type:Array,
required:true// 表示该值是必须的,没有将
// 会报错
}}, //第二步.通过props 这里 接受 msg 的值
data(){
return{
msg1:'world',
count:0
}
},
components:{
myConn
},
methods:{
show(){
return this.msg + this.msg1
},
mydemo(data){
this.count += data
},
changen(){
this.count++
},
two(){
console.log('这是myMain中的two()')
this.$refs.aaa.changeone()
this.$refs.bbb.changeone()
console.log(this.$refs.aaa.num)
console.log(this.$refs.bbb.num)
}
}
}
</script>
3. 插槽
当多个类似组件有80%相同,仅20%不同时,可以用插槽
mybar.vue >>
<template>
<div class="mybar">
<h6>{{title}}</h6>
<slot></slot>
</div>
</template>
<script>
export default {
name: "myBar",
data(){
return{
title:'mybar'
}
},
methods:{
add(x,y){
return x+y
}
}
}
</script>
<style scoped>
.mybar{
width: 80%;
height: 70px;
margin: 10px;
background-color: yellow;
}
</style>
mySidebar.vue >>
<template>
<my-bar>
<button>提交</button>
</my-bar>
<my-bar>
<a href="">提交</a>
</my-bar>
<my-bar>
<p><span>111</span><b>222</b></p>
</my-bar>
</template>
<script>
import myBar from "./childComp/myBar";
export default {
name: "mySidebar",
components:{
myBar
}
}
</script>
<style scoped>
</style>
具名插槽
可以指定,另外还需要注意作用域的问题,使用slot不要直接{{}}
mybar.vue >>
<template>
<div class="mybar">
<h6>{{title}}</h6>
<slot name="one"><button>提交</button></slot>
<slot name="two" :user2="user">11111</slot>
<slot :user="user"></slot>
</div>
</template>
<script>
export default {
name: "myBar",
data(){
return{
title:'mybar',
user:{name: 'eduwork'}
}
},
methods:{
add(x=1,y=2){
return x+y
}
}
}
</script>
<style scoped>
.mybar{
width: 80%;
height: 70px;
margin: 10px;
background-color: yellow;
}
</style>
mySidebar.vue >>
<template>
<my-bar>
<template v-slot:one>
<a href="">{{name}}</a>
</template>
</my-bar>
<my-bar>
<template #two>
<a href="">提交</a>
</template>
</my-bar>
<my-bar>
<template v-slot:two="hello">
<a href="">{{hello.user2.name}}</a>
</template>
<template v-slot:default="subdata">
<a href="">{{subdata.user}}</a>
</template>
</my-bar>
</template>
<script>
import myBar from "./childComp/myBar";
export default {
name: "mySidebar",
data(){
return{
name:'hello'
}
},
components:{
myBar
}
}
</script>
<style scoped>
</style>
4. Vuex 状态管理
Vuex 状态管理 相对于 超全局变量(加强版的data),可以响应式,
- 可以携带token,用户的登录状态,类似cookie,session(),多个组件共享的数据
- 商品的收藏、购物车中的物品
- 传递的层数太多
Vuex是vue的插件
6 vue请求axios
7 vue-router
vue使用vue-router来实现SPA的插件
编写路由的3步
-
1. 定义路由组件
-
2. 映射路由
-
3. 编写路由2个标签
1. 创建路由器: router/index.js
这里是路由的配置文件,是映射关系,真正使用router是在main.js里
import { createRouter, createWebHistory } from 'vue-router'
//3 把路由关系包含进来(下面两种都一样)
// 3.1 import方法
// import About from '../views/About'
// import Home from '../views/Home'
// import User from '../views/User.vue'
// 3.2 懒加载方式:
const Home = () => import(/* webpackChunkName: "about" */ '../views/Home')
const About = () => import(/* webpackChunkName: "about" */ '../views/About')
const User = () => import(/* webpackChunkName: "about" */ '../views/User')
//2 路由规则
const routes = [
{
// 根目录下面是home,加载的组件就是上面的Home
path: '/',
name: 'Home',
// component: Home
components:{
default:Home,
About,
User
}
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
components:{
default:Home,
User,
About,
}
},
{
path: '/user',
name: 'User',
component: User
}
]
//1.创建路由
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),//l历史模式
routes
})
//3 暴露路由出去
export default router
2. 注册路由器: main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
//创建router后,应用早App上, 挂载在'#app'这个实例上了
createApp(App).use(router).mount('#app')
3. 使用路由组件标签: App.vue
<template>
<div id="nav">
<!--路由自带的标签 : <router-link ></router-link>
这里 to 是指加载到哪个模板
-->
<router-link class="bg" active-class="active" to="/">首页</router-link> |
<router-link class="bg" to="/about">关于我们</router-link> |
<router-link class="bg" to="/user">个人中心</router-link>
|
<!-- 插槽可以用-->
<router-link to="/about" custom v-slot="{navigate}">
<button @click="navigate" @keypress="navigate" role="link">按钮</button>
</router-link>
|
<!--加入全局属性-->
<buttom @click="$router.push('/user')">个人中心</buttom>
|
<buttom :class="{active:$route.path=='/user'}" @click="$router.go(-1)">返回</buttom>
|
{{$router.path}}
</div>
<hr width="100%">
<router-view class="one" name="User"></router-view>
<router-view class="two" ></router-view>
<router-view class="three" name="About"></router-view>
</template>
<style lang="scss">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
}
}
.active{
color: red !important;
}
.bg{
background-color: honeydew;
}
}
.one{
width: 25%;
height: 300px;
background-color: #f0fff0;
float: left;
}
.two{
width: 50%;
height: 300px;
background-color: #888888;
float: left;
}
.three{
width: 25%;
height: 300px;
background-color: #f0fff0;
float: right;
}
</style>
2. 嵌套路由(子路由)
index.js
//0.这里是路由的配置文件,是映射关系,真正使用router是在main.js里
import { createRouter, createWebHistory } from 'vue-router'
//3 把路由关系包含进来(下面两种都一样)
//3.1
// import About from '../views/About'
// import Home from '../views/Home'
// import User from '../views/User.vue'
//3.2 懒加载方式:
const Home = () => import(/* webpackChunkName: "about" */ '../views/Home')
const About = () => import(/* webpackChunkName: "about" */ '../views/About')
const User = () => import(/* webpackChunkName: "about" */ '../views/User')
const MyOrder = () => import('../views/MyOrder')
const MySetting = () => import('../views/MySetting')
//2 路由规则
const routes = [
{
// 根目录下面是home,加载的组件就是上面的Home
path: '/',
name: 'Home',
// component: Home
component:Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: About,
},
{
path: '/user',
name: 'User',
component: User,
//5 子路由
children:[
//5.2 默认显示子路由中的哪一个
{
path: '',
component:MyOrder
},
//5.1 /user/order
{
path:'order',
component:MyOrder
},
// /user/setting
{
path: 'setting',
component:MySetting
}
]
}
]
//1.创建路由
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),//l历史模式
routes
})
//3 暴露路由出去
export default router
user.vue
在user里加入子路由
向路由组件传递数据
params: <router-link to="/home/news/abc/123">
props: <router-view msg='abc'>
<template>
<div>这是个人中心页面</div>
<br>
<div class="menu">
<ul>
<li><router-link to="/user/order">我的订单</router-link></li>
<li><router-link to="/user/setting">我的设置</router-link></li>
</ul>
</div>
<div class="content">
<router-view/><!-- 这里是可以显示路由的内容-->
</div>
</template>
<script>
export default {
name: "User"
}
</script>
<style scoped>
.menu{
width: 30%;
height: 300px;
background-color: honeydew;
float: left;
}
.content{
width: 70%;
height: 300px;
background-color: white;
float: right;
}
a {
font-weight: bold;
color: #2c3e50;
}
a.router-link-exact-active {
color: #42b983;
}
</style>
3. 传递参数的方式
向路由组件传递数据
params: <router-link to="/home/news/abc/123">
props: <router-view msg='abc'>
user.vue
<template>
<div>这是个人中心页面</div>
<br>
<div class="menu">
<ul>
<li>
<router-link to="/user/order">我的订单</router-link>
</li>
<li>
<router-link to="/user/setting">我的设置</router-link>
</li>
<li>
<router-link to="/user/page/1">单页一</router-link>
</li>
</ul>
<ul>
<li v-for="item in artical">
<router-link :to="'/user/page/'+item.id">{{item.title}}</router-link>
</li>
</ul>
<li>
<router-link to="/user/article?name=111&age=10">文章一</router-link>
</li>
<li>
<router-link :to="{path:'/user/article',query:{name:'hello',age:10}}">文章二</router-link>
</li>
<br>
<button @click="$router.push({path:'/user/article',query: {name:'world',age: 100}})">文章三</button>
</div>
<div class="content">
<router-view/><!-- 这里是可以显示路由的内容-->
</div>
</template>
<script>
export default {
name: "User",
data() {
return {
artical: [
{id: 10, title: 'aaa'},
{id: 11, title: 'bbb'},
{id: 12, title: 'ccc'},
{id: 13, title: 'ddd'},
{id: 14, title: 'eee'}
]
}
}
}
</script>
<style scoped>
.menu {
width: 30%;
height: 300px;
background-color: honeydew;
float: left;
}
.content {
width: 70%;
height: 300px;
background-color: white;
float: right;
}
a {
font-weight: bold;
color: #2c3e50;
}
a.router-link-exact-active {
color: #42b983;
}
</style>
MyPage.vue
<template>
<div>
<h2>这是文章的模板 </h2>
<!--这里可以获取参数-->
文章ID:{{$route.params.id}}
<br>
<!-- 插值计算-->
<h1>{{pageid}}</h1>
</div>
</template>
<script>
export default {
name: "MyPage",
computed:{
pageid(){
return this.$route.params.id
}
/* 上面是缩写
pageid:{
get(){
}
}
*/
}
}
</script>
<style scoped>
</style>
MyArticle.vue
<template>
<div>
<h2>这是文章的页面</h2> <br>
name:{{$route.query.name}} <br>
age: {{$route.query.age}} <br>
</div>
</template>
<script>
export default {
name: "MyArticle"
}
</script>
<style scoped>
</style>
index.js
//0.这里是路由的配置文件,是映射关系,真正使用router是在main.js里
import { createRouter, createWebHistory } from 'vue-router'
//3 把路由关系包含进来(下面两种都一样)
//3.1
// import About from '../views/About'
// import Home from '../views/Home'
// import User from '../views/User.vue'
//3.2 懒加载方式:
const Home = () => import(/* webpackChunkName: "about" */ '../views/Home')
const About = () => import(/* webpackChunkName: "about" */ '../views/About')
const User = () => import(/* webpackChunkName: "about" */ '../views/User')
const MyOrder = () => import('../views/MyOrder')
const MySetting = () => import('../views/MySetting')
const MyPage = () => import('../views/MyPage')
const MyArticle = () => import('../views/MyArticle')
//2 路由规则
const routes = [
{
// 根目录下面是home,加载的组件就是上面的Home
path: '/',
name: 'Home',
// component: Home
component:Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: About,
},
{
path: '/user',
name: 'User',
component: User,
//5 子路由
children:[
//5.2 默认显示子路由中的哪一个
{
path: '',
component:MyOrder
},
//5.1 /user/order
{
path:'order',
component:MyOrder
},
// /user/setting
{
path: 'setting',
component:MySetting
},
// /user/page/*
// 传参params方法
{
path: 'page/:id',//注意这里一定page 后面要加 /,才能跳转
component:MyPage
},
{
path: 'article',
component:MyArticle
}
]
}
]
//1.创建路由
const router = new createRouter({
history: createWebHistory(process.env.BASE_URL),//l历史模式
routes
})
//3 暴露路由出去
export default router
4 缓存路由组件
<keep-alive>
<router-view></router-view>
</keep-alive>
5 路由的编程式导航
this.$router.push(path): 相当于点击路由链接(可以返回到当前路由界面)
this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面)
this.$router.back(): 请求(返回)上一个记录路由
8 购物车案例
<template>
<div>
<div v-if="cartList.length<=0">购物车是空的哦,看看商品加入购物车</div>
<table v-else>
<caption><h1>购物车</h1></caption>
<tr>
<th></th>
<th>id</th>
<th> name</th>
<th> price</th>
<th> count </th>
<th> delete </th>
</tr>
<tr v-for="(item,index) in cartList" :key="item.id">
<td><input type="checkbox" v-model="item.checkbox"></td>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td><small>¥</small>{{item.price.toFixed(2)}}</td>
<td>
<button @click="item.count--" :disabled="item.count<=1">-</button>
{{item.count}}
<button @click="item.count++">+</button>
</td>
<td><a href="#" @click.prevent="del(index)">delete</a></td>
</tr>
<tr>
<td colspan="3" align="right">total price</td>
<td colspan="3">{{totalPrice}}</td>
</tr>
</table>
</div>
</template>
<script>
export default {
name: "App",
data(){
return {
cartList:[
{id:1,checkbox:'true',name:'macbook',price:7000,count:1},
{id:2,checkbox:'true',name:'iphone',price:5000,count:1},
{id:3,checkbox:'true',name:'air-condition',price:2000,count:1},
{id:4,checkbox:'true',name:'iwatch',price:1000,count:1},
{id:5,checkbox:'true',name:'ipencil',price:700,count:1},
{id:6,checkbox:'true',name:'ipad',price:4000,count:1},
]
}
},
computed:{
totalPrice:{
get(){
let sum = 0
for (const book of this.cartList) {
if (book.checkbox) {
sum += book.price*book.count
}
}
return '¥'+sum.toFixed(2)
}
}
},
methods:{
del(index){
this.cartList.splice(index,1)
}
}
}
</script>
<style scoped>
table{
width: 600px;
border: 1px solid gainsboro;
border-collapse: collapse;
}
th{
background-color: pink;
}
td,th{
border: 1px solid gainsboro;
padding: 10px;
}
</style>
ref
-
被用来给元素或子组件注册引用信息(id的替代者)
-
应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
-
使用方式:
- 打标识:
<h1 ref="xxx">.....</h1>
或<School ref="xxx"></School>
- 获取:
this.$refs.xxx
- 打标识:
props
props的优先级高于data中 0. 功能:让组件接收外部传过来的数据
-
传递数据:
<Demo name="xxx"/>
-
接收数据:
-
第一种方式(只接收):
props:['name']
-
第二种方式(限制类型):
props:{name:String}
-
第三种方式(限制类型、限制必要性、指定默认值):
props:{ name:{ type:String, //类型 required:true, //必要性 default:'老王' //默认值 } }
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
-
mixin 混入
功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步定义混合:
```
{
data(){....},
methods:{....}
....
}
```
第二步使用混入:
全局混入:`Vue.mixin(xxx)` 局部混入:`mixins:['xxx']`
mixin.js
export const mixin = {
methods:{
showName(){
alert(this.name)
}
},
mounted() {
console.log('hello~~~')
}
}
export const mixin2 = {
data(){
return{
x:100,
y:200
}
},
}
Stud.vue
<template>
<div>
<h2 @click="showName">name:{{name}}</h2>
<h2>lesson:{{lesson}}</h2>
</div>
</template>
<script>
//引入mixin
import {mixin, mixin2} from '../mixin'
export default {
name: "Stud",
data(){
return{
name:'tom',
lesson:'PE'
}
},
mixins:[mixin,mixin2]
}
</script>
<style scoped>
</style>
插件 plugins
-
功能:用于增强Vue
-
本质
:包含install方法的一个对象
,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。 -
定义插件:
对象.install = function (Vue, options) { // 1. 添加全局过滤器 Vue.filter(....) // 2. 添加全局指令 Vue.directive(....) // 3. 配置全局混入(合) Vue.mixin(....) // 4. 添加实例方法 Vue.prototype.$myMethod = function () {...} Vue.prototype.$myProperty = xxxx }
plugins.js
export default {
install(Vue,x,y,z){
console.log(x,y,z)
// console.log('@@@install',Vue )
// 全局过滤器
Vue.filter('mySlice',function (value) {
return value.slice(0,4)
})
// 定义全局指令
Vue.directive('fbind',{
bind(element,binding){
element.value = binding.value
},
inserted(elememt){
elememt.focus()
},
update(element,binding){
element.value = binding.value
}
})
// 定义混入mixin
Vue.mixin({
data(){
return{
x:200,
y:100
}
}
})
//给Vue原型上添加一个hello方法(vm 和 vc 都能用了)
Vue.prototype.hello = ()=>{alert('hello~~~')}
}
}
- 使用插件:
Vue.use()
main.js
import Vue from "vue";
import App from "./App";
import plugins from "./plugins";//引入插件
Vue.config.productionTip = false
//使用插件
Vue.use(plugins,1,2,3)
new Vue({
el: '#app',
render: h => h(App)
})
组件中应用具体插件
Stud.vue
<template>
<div>
<h2 >school_name:{{name | mySlice}}</h2>
<h2>address:{{address}}</h2>
<button @click="test">test</button>
</div>
</template>
<script>
export default {
name: "Schol",
data(){
return{
name:'aqhdxaaaaaa',
address:'beijing'
}
},
methods:{
test(){
this.hello()
}
}
}
</script>
<style scoped>
</style>
9 Vue 生命周期
vue实例从新建到销毁的一个完整流程,以及在这个过程中它会触发哪些生命周期的钩子函数
在谈到Vue的生命周期的时候,
先需要创建一个实例,也就是在 new Vue ( ) 的对象过程当中,首先执行了init,调用了beforeCreate,
然后在injections(注射)和reactivity(反应性)的时候,它会再去调用 created。
当created完成之后,它会去判断 实例 里面是否含有“el”option(选项),如果没有的话,它会调用vm.$mount(el)这个方法,然后执行下一步;如果有的话,直接执行下一步。
紧接着会判断是否含有“template”这个选项,如果有的话,它会把template解析成一个 render function ,render函数返回一个createElement方法,render函数是发生在beforeMount和mounted之间的,
beforeMount在有了render function的时候才会执行,当执行完render function之后,就会调用mounted这个钩子,在mounted挂载完毕之后,这个实例就算是走完流程了。
后续的钩子函数执行的过程都是需要外部的触发才会执行:
比如说有数据的变化,会调用beforeUpdate,
然后经过Virtual DOM,最后updated更新完毕。
当组件被销毁的时候,它会调用beforeDestory,以及destoryed。
myConn.vue >>
<template>
<div class="myConn">
<button @click="changeNum(2)">+</button>
{{mess}}
<button @click="one">++</button>
<br>
{{num}}
<br>
<span v-for="item in article">{{item}}</span>
</div>
</template>
<script>
export default {
name: "myConn",
data(){
return{
mess:'this is main test',
num:0
}
},
props:{
article: {
type:Array
}
},
beforeUnmount() {
console.log('7 #####--- 实例在销毁之前调用')
},
unmounted() {
console.log('8 #####--- 实例销毁完成')
},
activated() {
console.log('@ 通过keep-alive 缓存之前调用')
},
deactivated() {
console.log('@ 缓存数据恢复调用')
this.$nextTick(()=>{
this.$refs.username.focus()
})
},
methods:{
changeNum (num){
this.$emit('mycountevent',num)
},
one(){
console.log('子组件myConn中one()')
this.$parent.changen()
console.log( this.$parent.count)
this.$parent.$parent.appmet()
console.log( this.$parent.$parent.msg)
this.$root.appmet()
},
changeone(){
this.num++
}
}
}
</script>
<style scoped>
.myConn{
width: 90%;
height: 100px;
background-color: aquamarine;
margin: 10px;
}
</style>
myMain.vue >>
<template>
<div style="width: 100px; height: 50px;background-color: blanchedalmond">
{{count}}
<button @click="two">让子组件+1</button>
<br>
<button @click="isShow = !isShow">销毁conn</button>
</div>
<my-conn v-if="isShow" ref="aaa" @mycountevent="mydemo"></my-conn><!-- ref="aaa" 相对于给这行组件起一个别名aaa -->
<my-conn ref="bbb" @mycountevent="mydemo" :article="article"></my-conn>
{{msg}}<!--第三步.最后在这里可以展示msg-->
{{show()}}
<br>
{{myTitle}} {{myTitle}}
<br>
{{article}}
<span v-for="item in article">{{item}}</span>
</template>
<script>
import myConn from "./childComp/myConn";
export default {
name: "myMain",
//props:[name1,name2,...]默认是这种形式
props:{
msg:{
type:String,
default:'####'// 默认是'####'
},
myTitle:{
type:String
},
article:{
type:Array,
required:true// 表示该值是必须的,没有将
// 会报错
}}, //第二步.通过props 这里 接受 msg 的值
data(){
return{
msg1:'world',
count:0,
isShow:true
}
},
beforeCreate() {
console.log('1 --- 创建实例之前自动调用beforeCreate')
},
created() {
console.log('2 --- 实例创建完成 create()')
},
beforeMount() {
console.log('3 --- 模板编译之前 beforeMount')
},
mounted() {
console.log('4 --- 模板编译完成 mounted')
},
beforeUpdate() {
console.log('5 --- 模板更新之前')
},
updated() {
console.log('6 --- 模板内容跟新完成')
},
beforeUnmount() {
console.log('7 --- 实例在销毁之前调用')
},
unmounted() {
console.log('8 --- 实例销毁完成')
},
components:{
myConn
},
methods:{
show(){
return this.msg + this.msg1
},
mydemo(data){
this.count += data
},
changen(){
this.count++
},
two(){
console.log('这是myMain中的two()')
this.$refs.aaa.changeone()
this.$refs.bbb.changeone()
console.log(this.$refs.aaa.num)
console.log(this.$refs.bbb.num)
}
}
}
</script>
<style scoped>
</style>