Vue2 基础知识笔记

187 阅读7分钟

写在前面: 知识总结于:尚硅谷

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

事件监听

截屏2021-07-15 08.59.54.png

事件的基本使用:

  • 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" image.png

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 组件化开发

截屏2021-07-15 16.21.56.png

截屏2021-08-01 15.59.26.png

截屏2021-08-01 15.59.52.png

5 组件通信

1. 父子组件通信

父子组件之间不能直接相互访问data内容

IMG_1070.JPG

父组件 通过 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>

下面是完整的

截屏2021-08-02 09.45.14.png

  • 注 : 注意这里: :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;

截屏2021-08-02 10.14.54.png

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. 插槽

截屏2021-07-26 10.36.06.png 当多个类似组件有80%相同,仅20%不同时,可以用插槽

截屏2021-08-02 10.44.50.png

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不要直接{{}}

截屏2021-08-02 11.10.13.png

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

截屏2021-07-26 10.45.04.png

截屏2021-07-26 10.45.39.png

7 vue-router

vue使用vue-router来实现SPA的插件

截屏2021-07-26 10.48.45.png

编写路由的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')

截屏2021-08-18 10.31.46.png

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. 嵌套路由(子路由)

截屏2021-08-18 10.21.39.png

截屏2021-08-18 10.21.58.png 截屏2021-08-18 10.31.13.png

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'>

截屏2021-08-18 10.25.21.png

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

截屏2021-08-18 11.14.22.png

4 缓存路由组件

<keep-alive>
  <router-view></router-view>
</keep-alive>

5 路由的编程式导航

this.$router.push(path): 相当于点击路由链接(可以返回到当前路由界面)
this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面)
this.$router.back(): 请求(返回)上一个记录路由

8 购物车案例

截屏2021-07-15 15.23.58.png

<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

  1. 被用来给元素或子组件注册引用信息(id的替代者)

  2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)

  3. 使用方式:

    1. 打标识:<h1 ref="xxx">.....</h1><School ref="xxx"></School>
    2. 获取:this.$refs.xxx

props

props的优先级高于data中 0. 功能:让组件接收外部传过来的数据

  1. 传递数据:<Demo name="xxx"/>

  2. 接收数据:

    1. 第一种方式(只接收):props:['name']

    2. 第二种方式(限制类型):props:{name:String}

    3. 第三种方式(限制类型、限制必要性、指定默认值):

      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

  1. 功能:用于增强Vue

  2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

  3. 定义插件:

    对象.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~~~')}
  }
}
  1. 使用插件: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 生命周期

截屏2021-07-26 10.38.30.png

1130688-20190712171333587-104207991.png

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>

截屏2021-08-02 12.21.06.png