07vue笔记

0.资料地址

0.1视频地址

https://www.bilibili.com/video/BV1Zy4y1K7SH?p=4&spm_id_from=pageDriver

0.2笔记地址

https://blog.csdn.net/weixin_44972008/category_10622253.html

1.初识Vue

1.1特点

  • 组件化模式
每一个模块一个vue组件,每一个vue都有html+css+js
  • 声明式编码
不需要直接使用Document操作
  • 使用虚拟DOM+优秀比较算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MzmpLXTC-1646275953129)(https://secure2.wostatic.cn/static/jSeZEipadsVdfmFrfTg6Pd/image.png?auth_key=1644978426-h9ismKpgtrZDWT7MHTK1a2-0-e8320e19c82a14efeb1f472062fb51b9&image_process=resize,w_1516/format,webp)]

主要用于更新数据中,如下图,已存在DOM经过比较无变化,则不更新,只更新新增的

1.2安装

  • 地址
https://cn.vuejs.org/v2/guide/installation.html#%E7%9B%B4%E6%8E%A5%E7%94%A8-lt-script-gt-%E5%BC%95%E5%85%A5

//脚手架安装npm,先不使用
//使用直接用 <script> 引入
//建议安装开发版本,包含完整的警告和调试模式

1.3使用

  • 创建项目,引入vue.js
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 引入vue -->
    <script src="../js/vue.js"></script>
</head>
<body>
</body>
</html>
  • 右键浏览器打开,打开控制台console

两个报错及解决方式:

1)没有vue开发者工具

1、下载vue_dev_tools.crx开发者工具
2、打开浏览器--扩展程序,打开开发者模式,将文件拖到里面

注意;
	如果错误提示还没有消失,
	点击扩展程序的详情,打开【无痕使用】以及【允许访问文件网址】选项
  • 2)开发环境vue提示
1、打开vue官网,选择学习--API--全局配置--productionTip
	https://cn.vuejs.org/v2/api/
2.配置
<body>
    <script>
        Vue.config.productionTip = false
    </script>
</body>

1.4案例

  • 代码
<!--
  1.想让vue工作就需要创建vue对象,且传入参数
  2.vue接管的元素依旧符合html规范,但是要同时满足vue语法
  3.vue接管的元素叫root容器,内叫做【vue模板】
-->

<body>
    <div id="root">
        <h1>1111,{{name}}</h1><!--使用双花括号作为vue的数据变量,范围仅限vue接管的元素-->
    </div>
    <script>
        Vue.config.productionTip = false		//关闭生产提示
        const x = new Vue({
            el:'#root',	//el指定哪个元素作为vue的使用元素,也可以用js找到该元素
            data:{		//暂时使用data作为数据源
                name: 'hello'
            }
        })
    </script>
</body>
  • 注意事项
1、容器与Vue实例 一一对应
2{{}}可以是【vue实例中的数据】,也可以是【js表达式】,vm及原型所有东西也可以用
	例如:Date.now(), a, a+b, fun(a), a===b?'a':'b'3、data数据变化,对应页面数据随之改变

2.基础语法

2.1 模板语法

  • 插值语法&指令语法
1.插值语法
	# {{}}
	
2.指令语法
	# v-bind:xxx='yyy'  // yyy会作为表达式解析执行
	# 简写::xxx='yyy'
	
使用
	# 插值语法一般用在【标签体】里面
	# 指令语法一般用于【标签】中
  • 代码
<div id="root">
    <!-- 1 插值 -->
    <h1>1111,{{name}}</h1>
    
    <!-- 2 指令 -->
    <!-- <a v-bind:href="url"></a> -->
    <a :href="url">跳转</a>
</div>
<script>
    new Vue({
        el:'#root',
        data:{
            name: 'hello',
            url:'http://www.baidu.com/'
        }
    })    
</script>

2.2 数据绑定

1)单向绑定&双向绑定

<div id="root">
    <!-- 单项数据绑定:<input type="text" v-bind:value="name"><br/> -->
    单项数据绑定:<input type="text" :value="name"><br/>
    
    <!-- 双向数据绑定:<input type="text" v-model:value="name"> -->
    双向数据绑定:<input type="text" v-model="name">
</div>
<script>
    const vm = new Vue({
        el:'#root',
        data:{
            name: 'hello',
        }
    })
</script>

2)总结

1.单向绑定(v-bind):
	# 数据只能从data流向页面
2.双向绑定:
	# 数据不仅可以从data流向页面,也可以从页面流向data
注意:
	1.双向绑定一般用于表单类元素(input,select等)  #标签中必需要有value属性
	2.v-model默认收集value值,可以简写# v-model:value --> v-model

2.3 el&data两种写法

1)el

<script>
    Vue.config.productionTip = false
    const vm = new Vue({
        //el:'#root',     //第一种写法
    })
    x.$mount('#root'),		//第二种写法
</script>

2)data

<script>
    const vm = new Vue({
        //第一种 对象式
        // data:{
        //     name: 'hello',
        // }
        
        //第二种 函数式
        data:function() {  //:function可以省略
            return {
                name: 'hello',
            }
        },
    })
</script>

3)注意

1.组件使用时,data必须使用函数式
2.由Vue管理的函数,一定不要写成箭头式,箭头式指的是window,普通函数指的是Vue

2.4 MVVM模型

1)介绍

  • M 模型(Model) :data中的数据
  • V 视图(View) :模板代码(不是静态页面) (两个语法:指令,大括号表达式)
  • VM viewModel: 视图模型(Vue的实例)
    • Dom Listeners (Dom 监听
    • Data Bindings (数据绑定

发现

  1. data中所有的属性,最后都出现在了vm身上。
  2. vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用
MVVM 本质上是 MVC (Model-View- Controller)的改进版。即模型-视图-视图模型。

# 模型model指的是后端传递的数据,
# 视图view指的是所看到的页面。
#视图模型viewModel是 mvvm 模式的核心,它是连接 view 和 model 的桥梁。它有两个方向:
	1.将模型转化成视图,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定
	2.将视图转化成模型,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听
	这两个方向都实现的,我们称之为数据的双向绑定

2.5数据代理

1)方法Object.defineProperties()

  • 用处(对象添加属性)
1.数据劫持
2.数据监听
3.计算属性
  • 代码
<script>
    let number = 19
    let person = {
        name:'monkey',
        // age:16
    }
    Object.defineProperty(person, 'age', {
        // value:20,
        // enumerable:true,    //是否可以被枚举(遍历里面数据),默认false
        // writable:true,      //是否可以被修改
        // configurable:true,   //是否可以被删除

        get(){
            return number
        },
        set(value){
            number = value
        }
    })
</script>

2)数据代理

1.vue数据代理
	通过vm对象来代理data对象中的属性进行操作(读、写)
2.好处
	更加方便操作data中的数据
3.【原理】
	# 通过Object.defineProperty()把data对象添加到vm上
	# 每一个属性都添加到vm上的属性,都指定一个getter/setter
	# 使用getter/setter去操作(读/写)data中的数据

2.6事件处理

1)事件使用

<div id="root">
    <!-- <button v-on:click="show1"></button>  可以简写[v-on: --> @} -->
    <button @click="show1()">不带参数</button>
    <button @click="show2($event, 66)">带参数</button>
</div>
<script>
    const vm = new Vue({
        el:'#root',     
        methods: {
            show1(){
                console.log("123")
            },
            show2(envent, number){
                console.log(number)
                console.log(event.target.innerText)
            }
        },
    })
</script>

总结

1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;
2.事件的回调需要配置在methods对象中,最终会在vm上;
3.methods中配置的函数,不要用箭头函数!否则this就不是vm了;
4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
5.@click="demo"和 @click="demo($event)"效果一致,但后者可以传参;

2)事件修饰符

  • 介绍
1.prevent:阻止默认事件(常用);event.preventDefault()
2.stop:阻止事件冒泡(常用);event.stopPropagation()
3.once:事件只触发一次(常用);
4.capture:使用事件的捕获模式;
5.self:只有event.target是当前操作的元素时才触发事件;
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
  • 代码
<div id="root">
    <a href="http://www.baidu.com/" @click.prevent="show1">1.阻止跳转页面</a>
    <div @click="show1()">
        <button @click.stop="show1()">2.阻止冒泡(叠加执行多次)</button>
    </div>
    <button @click.once="show1()">3.只执行一次</button> 
</div>
<script>
    const vm = new Vue({
        el:'#root',     
        methods: {
            show1(){
                alert("123")
            }
        }
    })
</script>

3)键盘事件

  • 介绍
1.常见
    # 回车 => 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.自定义键名 = 键码,可以去定制按键别名
  • 代码
<div id="root">
    输入(常见的):<input type="text" placeholder="按下回车提示" @keyup.enter="show1"><br>
    输入(自定义):<input type="text" placeholder="按下切换大小写提示" @keyup.caps-lock="show2"><br>
    输入(自定义按下触发):<input type="text" placeholder="按下tab提示" @keydown.tab="show1">
</div>
<script>
    const vm = new Vue({
        el:'#root',     
        methods: {
            show1(){
                alert("123")
            },
            show2(e){
                console.log(e.key, e.keyCode),  //获取键盘名称和code值
                    alert("123")
            }
        },
    })
</script>

4)小技巧

#修饰符连写
@keyup.stop.prevent = 'xxx'
#键盘事件连写
@keyuo.ctrl.y = 'xxx'

2.7计算属性

1)使用

  • 代码
<div id="root">
    姓:<input type="text" v-model='fristName' ><br>
    名:<input type="text" v-model='lastName' ><br>
    <span>姓名:{{fullName}}</span>
</div>
<script>
    Vue.config.productionTip = false
    const vm = new Vue({
        el:'#root',     
        data:{
            fristName: '张',
            lastName: '三',
        },
        computed:{
            //1.原来写法
            // fullName:{
            //     get(){
            //         return this.fristName + '-' + this.lastName
            //     },
            //     set(value){
            //         console.log(this)
            //         const arr = value.split('-')
            //         this.fristName = arr[0]
            //         this.lastName = arr[1]
            //     }
            // }
            //2.简写,默认只有getter方法
            fullName(){
                return this.fristName + '-' + this.lastName
            }
        }
    })
</script>
  • 总结
1。定义:要用的属性不存在,要通过已有属性计算得来。
2。原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
3。get函数什么时候执行?
	(1). 初次读取时会执行一次。
	(2). 当依赖的数据发生改变时会被再次调用。
4。优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
5。备注:
	1.计算属性最终会出现在vm上,直接读取使用即可。
	2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。

2.8 监视属性

1)使用

  • 代码
<div id="root">
    <h1>天气很{{info}}</h1>
    <button @click="exchange">切换</button>
</div>
<script>
    Vue.config.productionTip = false
    const vm = new Vue({
        el:'#root',     
        data:{
            isHot: true
        },
        methods: {
            exchange(){
                this.isHot = !this.isHot
            }
        },
        computed:{
            info(){
                return this.isHot?'炎热':'寒冷'
            }
        },
        //第一种写法
        // watch:{
        //     isHot:{
        //         immediate: true,    //初始化调用handler
        //         handler(news, old){ //数据变化时被检测
        //            console.log(old + "被修改了" + news) 
        //         }
        //     }
        // }
    })
    //第二种写法(监视属性要加引号)
    vm.$watch('isHot', {    
        immediate: true,    //初始化调用handler
        handler(news, old){ //数据变化时被检测
            console.log(old + "被修改了" + news) 
        }
    })
</script>
  • 总结
1.通过通过vm对象的$watch()或watch配置来监视指定的属性
2.当属性变化时, 回调函数自动调用, 在函数内部进行计算
	当被监视的属性变化时, 回调函数自动调用, 进行相关操作
	监视的属性必须存在,才能进行监视!!
	监视的两种写法:
		(1). new Vue时传入watch配置
		(2). 通过vm.$watch监视

2)深度监视

<script>
    vm.$watch('number', {  
        deep:true,	//深度监视  
        handler(news, old){ 
        console.log(old + "被修改了" + news) 
        }
    })
</script>
深度监视:
	(1). Vue中的watch默认不监测对象内部值的改变(一层)。
	(2). 配置deep:true可以监测对象内部值改变(多层)。
备注:
	(1). Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
	(2). 使用watch时根据数据的具体结构,决定是否采用深度监视。

3)简写

//1.简写(没有其他属性)
watch:{
	isHot(news, old){
		console.log(old + "被修改了" + news) 
	}
}
//2.$watch
vm.$watch('number.a', function(news, old){
		console.log(old + "被修改了" + news) 
	}
)

4)computed&watch

1.computed和watch之间的区别:
	# computed能完成的功能,watch都可以完成。
	# watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作(定时器)。
	
2.两个重要的小原则:
	# 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
	# 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。

2.9 绑定样式

1)绑定class样式

<head>
    <style>
            .basic {
              width: 400px;
              height: 100px;
              border: 1px solid black;
            }
        
            .happy {
              border: 4px solid red;
              background-color: rgba(255, 255, 0, 0.644);
              background: linear-gradient(30deg, yellow, pink, orange, yellow);
            }
        
            .sad {
              border: 4px dashed rgb(2, 197, 2);
              background-color: gray;
            }
        
            .normal {
              background-color: skyblue;
            }
          </style>
</head>
<body>
    <div id="root">
        <!-- 字符串写法,  适用于:类名不确定,需要动态绑定 -->
        <div class="basic" :class="calssStr" @click="changeStr">{{name}}</div><br>
        <!-- 数组写法,  适用于:个数不确定,名字也不确定 -->
        <div class="basic" :class="calssArr" @click="changeArr">{{name}}</div><br>
        <!-- 对象写法,  适用于:个数,名字确定,动态确定用不用 -->
        <div class="basic" :class="calssObj">{{name}}</div><br>

    </div>
    <script>
        Vue.config.productionTip = false
        const vm = new Vue({
            el:'#root',     
            data:{
                name: '小张',
                calssStr: 'normal',
                calssArr: '',
                calssObj:{
                    sad: true,
                    happy: false,
                }
            },
            methods: {
                changeStr(){
                    this.calssStr = 'happy'
                },
                changeArr(){
                    const arr = ['normal', 'happy', 'bad']
                    const index = Math.floor(Math.random()*3)
                    this.calssArr = arr[index]
                }
            },
        })
    </script>
</body>

2)绑定style样式

<!-- 绑定style样式--对象写法 -->
<div class="basic" :style="styleObj, styleObj2">{{name}}</div>
<script>
const vm = new Vue({
    el: '#root',
    data: {
      name: 'hello',
      styleObj: {
        fontSize: '40px',
        color: 'red',
      },
      styleObj2: {
        backgroundColor: 'orange'
      },
    },
  })
</script>

2.10 条件渲染

1)v-if & v-show

<div id="root">
    <button @click="n++">数字{{n}}</button>
    <!-- v-if,  必须连续使用,页面不显示源代码不显示 -->
    <div v-if="n===1">v-if-1</div>
    <div v-else-if="n===2">v-if-2</div>
    <div v-else-if="n===3">v-if-3</div>
    <div v-else>v-if-4</div>
    <!-- template不改变原有的样式,只能搭配v-if -->
    <template v-if="n===0">
        <h2>111</h2>
        <h2>222</h2>
        <h2>333</h2>
    </template>

    <!-- v-show 默认显示display:none-->
    <div v-show="n===1">v-show</div>        
</div>
<script>
    Vue.config.productionTip = false
    const vm = new Vue({
        el:'#root',     
        data:{
            n: 0
        }
    })
</script>

2)总结

1.v-if
	#适用于:切换频率较低的场景。
	#特点:不展示的DOM元素直接被移除。
	#注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。
2.v-show
	#写法:v-show="表达式"
	#适用于:切换频率较高的场景。
	#特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉

#【备注】使用v-if的时候,元素可能无法获取到,而使用v-show一定可以获取到。

2.11列表渲染

1)遍历列表

<div id="root">
    <ul>
        <!-- 数组 -->
        <li v-for='(p, index) in personArr' :key="index">{{index}}: {{p.name}}--{{p.age}}</li><br>
    </ul>
    <ul>
        <!-- 对象 -->
        <li v-for='(v, k, index) in personObj' :key="index">{{index}}: {{k}}--{{v}}</li><br>
    </ul>
    <ul>
        <!-- 字符串 -->
        <li v-for='(pos, index) in personStr' :key="index">{{index}}: {{pos}}--{{index}}</li><br>
    </ul>
    <ul>
        <!-- 指定次数 -->
        <li v-for='(conunt, index) in 4' :key="index">{{index}}: {{conunt}}--{{index}}</li>
    </ul>
</div>
<script>
    Vue.config.productionTip = false
    const vm = new Vue({
        el:'#root',     
        data:{
            personArr:[
                {id:'001', name:'张三', age:18},
                {id:'002', name:'里斯', age:28},
                {id:'003', name:'王五', age:8}
            ],
            personObj:{
                id:'001', 
                name:'张三', 
                age:18
            },
            personStr:'abcd'
        },
    })
</script>

2)key作用及原理(难)

  1. 虚拟DOM中key的作用
    key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

  2. 对比规则:
    (1). 旧虚拟DOM中找到了与新虚拟DOM相同的key
    ①若虚拟DOM中内容没变, 直接使用之前的真实DOM
    ②若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM

    (2). 旧虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到到页面。

  3. 用index作为key可能会引发的问题
    (1). 若对数据进行:逆序添加、逆序删除等破坏顺序操作: 会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低
    (2). 如果结构中还包含输入类的DOM: 会产生错误DOM更新 ==>界面有问题

  4. 开发中如何选择key:
    (1). 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
    (2). 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

3)列表过滤

  • 代码
<div id="root">
    <input type="text" placeholder="输入名称" v-model="keyName">
    <ul>
        <li v-for='(p, index) in personShow' :key="index">{{index}}: {{p.name}}--{{p.age}}</li><br>
    </ul>
</div>
<script>
    Vue.config.productionTip = false
    const vm = new Vue({
        el:'#root',      
        data:{
            keyName:'',
            personArr:[
                {id:'001', name:'马冬梅', age:18},
                {id:'002', name:'周冬雨', age:28},
                {id:'003', name:'周杰伦', age:8},
                {id:'004', name:'周兆伦', age:8}
            ],
            // personShow:[],

        },
        //计算属性实现
        computed:{
            personShow(){
                return this.personArr.filter((p)=>{
                    return p.name.indexOf(this.keyName) !== -1
                })
            }
        },
        //监视属性实现
        // watch:{
        //     keyName:{
        //         immediate: true,    
        //         handler(value){
        //             this.personShow = this.personArr.filter((p)=>{
        //                 return p.name.indexOf(value) !== -1
        //             })
        //         }
        //     }
        // }
    })
</script>

4)列表排序

<div id="root">
    <input type="text" placeholder="输入名称" v-model="keyName">
    <button @click='sortType=2'>年龄升序</button>
    <button @click='sortType=1'>年龄降序</button>
    <button @click='sortType=0'>重置</button>
    <ul>
        <li v-for='(p, index) in personShow' :key="index">{{index}}: {{p.name}}--{{p.age}}</li><br>
    </ul>
</div>
<script>
    Vue.config.productionTip = false
    const vm = new Vue({
        el:'#root',      
        data:{
            keyName:'',
            personArr:[
                {id:'001', name:'马冬梅', age:38},
                {id:'002', name:'周冬雨', age:28},
                {id:'003', name:'周杰伦', age:19},
                {id:'004', name:'周兆伦', age:21}
            ],
            sortType:0,     //0原顺序 1倒叙 2正序
        },
        computed:{
            personShow(){
                const arr = this.personArr.filter((p)=>{
                    return p.name.indexOf(this.keyName) !== -1
                })
                if(this.sortType){
                    arr.sort((p1, p2)=>{
                        return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
                    })
                }
                return arr
            }
        },
    })
</script>

2.12监视数据原理

1)总结

1.vue会监视data中所有层次的数据。

2.如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
    (1).对象中后追加的属性,Vue默认不做响应式处理
    (2).如需给后添加的属性做响应式,请使用如下API:
        Vue.set(target,propertyName/index,value) 或
        vm.$set(target,propertyName/index,value)

3.如何监测数组中的数据?
	通过包裹数组更新元素的方法实现,本质就是做了两件事:
  	 	(1). 调用原生对应的方法对数组进行更新。
   		(2). 重新解析模板,进而更新页面。

4.在Vue修改数组中的某个元素一定要用如下方法:
	(1). 使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
	(2). Vue.set() 或 vm.$set()

# 特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!

2.13收集表单数据

1)代码

<div id="root">
    <form action="" @submit.prevent='submitInfo'>
        用户名:<input type="text" v-model.trim="name"><br>
        密码:<input type="password" v-model.trim="password"><br>
        年龄:<input type="number" v-model.number="age"><br>
        性别:男<input type="radio" id="male" value="" v-model="sex"><input type="radio" id="female" value="" v-model="sex"><br>
        爱好:篮球<input type="checkbox" id="basket" value="basket" v-model="likes">
        足球<input type="checkbox" id="foot" value="foot" v-model="likes">
        乒乓<input type="checkbox" id="pingpang" value="pingpang" v-model="likes"><br>
        城市:<select v-model="city">
        <option value="">--未选择--</option>
        <option value="xian">西安</option>
        <option value="beijiang">北京</option>
        <option value="shanghai">上海</option>
        <option value="guizhou">贵州</option>
        </select><br>
        介绍:<textarea rows="5" v-model.lazy="desc"></textarea><br>
        <input type="checkbox" v-model='agree'>阅读并接受<a href="http://www.baidu.com/">《用户协议》</a><br>
        <input type="submit" value="提交">
    </form>
</div>
<script>
    Vue.config.productionTip = false
    const vm = new Vue({
        el:'#root',      
        data:{
            name:'',
            password:'',
            age:'',
            sex:'男',
            likes:[],
            city:'',
            desc:'',
            agree:''
        },
        methods: {
            submitInfo(){
                console.log(this._data)
            }
        },
    })
</script>

2)总结

收集表单数据:
	若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。
	若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。
	若:<input type="checkbox"/>
		1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
		2.配置input的value属性:
			(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
			(2)v-model的初始值是数组,那么收集的的就是value组成的数组
	备注:v-model的三个修饰符:
		# lazy:失去焦点再收集数据
		# number:输入字符串转为有效的数字
		# trim:输入首尾空格过滤

2.14过滤器

1.定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
2.语法:
	1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
	2.使用过滤器:{undefined{ xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名”
备注:
	1.过滤器也可以接收额外参数、多个过滤器也可以串联
	2.并没有改变原本的数据, 是产生新的对应的数据

2.15内置指令

1)v-text

<div v-text='name'>你好,</div>		//会覆盖div里面原有的值
<div>你好,{{name}}</div>				//拼接起来(常用)

1)v-html

1作用:向指定节点中渲染包含html结构的内容。
2与插值语法的区别:
	(1). v-html会替换掉节点中所有的内容,{{xx}}则不会。
	(2). v-html可以识别html结构。
3严重注意:v-html有安全性问题!!!!
	(1). 在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
	(2). 一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!

1)v-cloak

指令(没有值)
	1.本质是一个特殊属性,Vue实例创建完成病接管容器后,会销毁
	2.使用css配合可以解决网速过慢时页面出现{{xxx}}问题
使用
	<h2 v-cloak>{{name}}</h2>
	[vocloak]{
		display:none
	}

2.8监视属性

1)v-once

1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。

1)v-pre

1.跳过其所在节点的编译过程。
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。

2.16自定义指令

2.17生命周期

1)mounted

<div id="root">
    <h2 :style="{opacity}">生命周期</h2>
</div>
<script>
    Vue.config.productionTip = false
    const vm = new Vue({
        el:'#root',    
        data:{
            opacity: 1,
        },
        mounted() {
            console.log('aa')
            setInterval(()=>{
                this.opacity -= 0.01
                if(this.opacity <= 0){
                    this.opacity = 1
                }
            }, 16)
        },
    })
</script>

2)生命周期

1 又名:生命周期回调函数、生命周期函数、生命周期钩子。
2 是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
3 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4 生命周期函数中的this指向是vm 或 组件实例对象。

1)总结

常用的生命周期钩子
	1.mounted(): 发送ajax请求, 启动定时器、绑定自定义事件、订阅消息等异步任务【初始化操作】
	2.beforeDestroy(): 做收尾工作, 如: 清除定时器、解绑自定义事件、取消订阅消息等【首尾工作】

销毁Vue实例
    1.销毁后借助Vue开发者工具看不到任何信息
    2.销毁后自定义事件会失效,但原生DOM事件依然有效
    3.一般不会在beforeDestroy操作数据,因为即使操作数据,也不会再触发更新流程了。

2.18组件

1)介绍

1.依赖关系混乱,不好维护
2.代码复用率不高

2)非单文件组件

  • 代码
<div id="root">
    <school></school>
    <hr>
    <student></student>
</div>
<div id="root1">
    <student></student>
</div>
<script>
    Vue.config.productionTip = false
    //school局部组件
    const school = Vue.extend({
        template:`
        <div>
        	<h2>学校名称:{{name}}</h2>
        	<h2>学校地址:{{addr}}</h2>
        </div>
        `,
        data(){
            return{
                name:'石沟子小学',
                addr:'西安'
            }
        }
    })
    //student局部组件
    const student = Vue.extend({
        template:`
        <div>
       	 	<h2>学生名称:{{name}}</h2>
        	<h2>学生年龄:{{age}}</h2>
        </div>
        `,
        data(){
            return{
                name:'张三',
                age:18
            }
        }
    })
    //注册全局组件
    Vue.component('student', student)
    //Vue实例
    const vm = new Vue({
        el:'#root',    
        components:{
            school,
            student
        }
    })
    //Vue实例使用全局组件
    new Vue({
        el:'#root1',    
    })
</script>
  • 总结
1.1 使用组件的三大步骤
	#定义组件(创建组件)
	#注册组件
	#使用组件(写组件标签)
	
1.2 如何定义一个组件
	使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但有以下区别
		#不要写el——最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
		#data必须写成函数——避免组件被复用时,数据存在引用关系
		【备注】使用tempalte可以配置组件结构

1.3 如何注册组件
	#局部注册:new Vue的时候传入components选项
	#全局注册:Vue.component(‘组件名’, 组件)
  • 注意点
1.关于组件名
	一个单词组成
		#第一种写法(首字母小写):school
		#第二种写法(首字母大写):School
	多个单词组成
		#第一种写法(kebab-case命名):my-school
		#第二种写法(CamelCase命名):MySchool(需要Vue脚手架支持)

2.备注
	#① 组件名尽可能回避HTML中已有的元素名称,例如h2、H2
	#② 可以使用name配置项指定组件在开发者工具中呈现的名字

3.关于组件标签
	#第一种写法:<school></school>
	#第二种写法:<school/> (不使用脚手架会导致后续组件不能渲染)

4.简写方式
	#const school = Vue.extend(options) 可以简写成 const school = options

3)VueComponent

1.app组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的

2.我们只需要写<app/><app></app>,Vue解析时会帮我们创建app组件的实例对象,即Vue帮我们执行new VueComponent(options)

3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent

4.关于this指向
	#① 组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】
	#② new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】

5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)

6.Vue的实例对象,以后简称为vm

4)重要的内置关系

5)单文件组件

  • School.Vue
<template>
    <div>
        <h2>学校名称:{{name}}</h2>
        <h2>学校地址:{{addr}}</h2>
    </div>
</template>
<script>
    export default {
        name:'School'
        data(){
            return{
                name:'石沟子小学',
                addr:'西安'
            }
        }
    }
</script>
  • App.Vue
<template>
    <div>
        <School></School>
    </div>
</template>
<script>
    import School from './School'
    export default {
        name:'App'
        components:{
            School,
        }
    }
</script>
  • main.js
import App from './App.Vue'
new Vue({
    el:'#root',
    components:{
        App
    }
})
  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="root">
        <App></App>
    </div>
    <script src="../../js/vue.js"></script>
    <script src="./main.js"></script>
</body>
</html>
  • 问题
1.无法运行
	报错:Uncaught SyntaxError: Cannot use import statement outside a module
	#需要安装脚手架运行

3.高级(使用脚手架)

3.1脚手架

1)安装

  • 1.设置淘宝镜像
npm config set registry https://registry.npm.taobao.org
  • 2.安装脚手架
npm install -g @vue/cl
  • 3.测试
vue
  • 4.切换项目路径,创建
vue create xxxx(项目名称)
  • 5.启动项目
npm run serve

2)目录结构

├── node_modules :库
├── public
│   ├── favicon.ico: 页签图标
│   └── index.html: 主页面
├── src
│   ├── assets: 存放静态资源
│   │   └── logo.png
│   │── component: 存放组件
│   │   └── HelloWorld.vue
│   │── App.vue: 汇总所有组件
│   │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件 
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件

3)render函数

render: h => h(App)
总结:
	#1.mian.js不能使用components:{App},脚手架引入的是残缺的vue,render函数处理

4)说明

1.关于不同版本的Vue
	vue.js与vue.runtime.xxx.js的区别:
		#vue.js是完整版的Vue,包含:核心功能 + 模板解析器。
		#vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
		#因为vue.runtime.xxx.js没有模板解析器,所以不能使用template这个配置项,需要使用render函数接收到的createElement函数去指定具体内容。
2.vue.config.js配置文件
	#使用vue inspect > output.js可以查看到Vue脚手架的默认配置。
	#使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh

3.2 小知识点

1)ref属性

<template>
    <div>
        <img src="./assets/logo.png" alt="login">
        <h2 v-text="msg" ref="info"></h2>
        <button @click="get" ref="but">获取dom元素</button>
        <School ref="comp"></School>
    </div>
</template>
<script>
    import School from './components/School.vue'
    export default {
        name:'App',
        data() {
            return {
                msg: 'hello',    
            }
        },
        methods: {
            get(){
                console.log(this.$refs.info)    //真实DOM对象   <h2>hello</h2>
                console.log(this.$refs.but)     //真实DOM对象   <h2>获取dom元素</h2>
                console.log(this.$refs.comp)    //School组件得实例对象(vc)    VueComponent
            }
        },
        components:{
            School,
        }
    }
</script>
  • 总结
1.被用来给元素或子组件注册引用信息(id的替代者)
2.应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3.使用方式:
	#打标识:<h1 ref="xxx">.....</h1>或 <School ref="xxx"></School>
	#获取:this.$refs.xxx

2)props配置

#父组件传参数
<Student name='张三' :age='18'></Student>
#子组件接受参数
<template>
    <div>
        <h2>学生名称:{{name}}</h2>
        <h2>学生年龄:{{age+1}}</h2>
    </div>
</template>
<script>
    export default {
        // props:['name', 'age'],  //1.简单接受
        // props:{                     //2.限制数据类型
        //     name:String,
        //     age:Number
        // },  
        props:{                     //3.限制数据类型+默认值+是否必填
            name:{
                type:String,
                required:true,
                default:'小花'
                },
            age:{
                type:Number,
            }
        },  
    }
</script>
  • 总结
1.功能:让组件接收外部传过来的数据

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

3.接收数据:
	#第一种方式(只接收):props:['name']
	#第二种方式(限制类型):props:{name:String}
	#第三种方式(限制类型、限制必要性、指定默认值):

#备注:props是只读的,Vue底层会监测你对props的修改,如果进行了【修改】,就会发出【警告】,若业务需求确实需要修改,那么请【复制】props的内容到data中一份,然后去修改data中的数据。

3)mixin混入

1)代码
  • 定义混合mixin.js
export const mixin = {
    methods: {
        show(){
            alert("hello!"+this.name)
        }
    },
}
  • 局部混合
<template>
    <div>
        <h2 @click="show">学生名称:{{name}}</h2>
    </div>
</template>
<script>
    import {mixin} from '../mixin.js'		//1.引入混合
    export default {
        mixins:[mixin] ,				//2.使用
    }
</script>
  • 全局混合mian.js
import Vue from 'vue'
import App from './App.vue'
import {mixin} from './mixin'       //1.引入混合

Vue.config.productionTip = false
Vue.mixin(mixin)					//2.使用

new Vue({
  render: h => h(App),
}).$mount('#app')

2)总结
全局混入:Vue.mixin(xxx)
局部混入:mixins:['xxx']

4)插件

  • 代码
export default {
  install(Vue, x, y, z) {
    console.log(x, y, z);
    //全局过滤器
    Vue.filter("mySlice", function (value) {
      return value.slice(0, 4);
    });
    //定义全局指令
    Vue.directive("fbind", {
      //指令与元素成功绑定时(一上来)
      bind(element, binding) {
        element.value = binding.value;
      },
      //指令所在元素被插入页面时
      inserted(element, binding) {
        element.focus();
      },
      //指令所在的模板被重新解析时
      update(element, binding) {
        element.value = binding.value;
      },
    });
    //定义混入
    Vue.mixin({
      data() {
        return {
          x: 100,
          y: 200,
        };
      },
    });
    //给Vue原型上添加一个方法(vm和vc就都能用了)
    Vue.prototype.hello = () => {
      alert("你好啊");
    };
  },
}

#使用
	import plugins from "./plugins";		//1.引入插件
	Vue.use(plugins, 1, 2, 3);				//2.使用
  • 总结
1.功能:用于增强Vue

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

5)scoped样式

  • 代码
<style scoped>		//标记scoped
    .demo{
        background-color:bisque;
    }
</style>
  • 总结
1.作用:让样式在局部生效,防止冲突。
2.写法:<style scoped>

3.3 练习案例

1)代码

  • TopInfo.vue
<template>
    <div class="todo-header">
          <input type="text" placeholder="请输入你的任务名称,按回车键确认" @keyup.enter="addLike"/>
    </div>
</template>

<script>
    import {nanoid} from 'nanoid'
    export default {
        name:'TopInfo',
        methods: {
            addLike(event){
                if(!event.target.value.trim()) return alert("输入不能为空!!!")
                const obj = {id:nanoid(), name:event.target.value, finished:false}
                this.getLike(obj)
                event.target.value=''
            }
        },  
        props:['getLike']
    }
</script>

<style scoped>
/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}
.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}

</style>
  • ListInfo.vue
<template>
    <ul class="todo-main">
        <ItemInfo v-for="like in LikesList" :key="like.id" :likeObj='like' :IsSelect='IsSelect' :isDelete='isDelete'></ItemInfo>
    </ul>
</template>

<script>
    import ItemInfo from './ItemInfo.vue'
    export default {
        name:'ListInfo',
        components:{
            ItemInfo
        },
        props:['LikesList', 'IsSelect', 'isDelete']
    }
</script>

<style scoped>
/*main*/
.todo-main {
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}
.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
</style>
  • ItemInfo.vue
/* eslint-disable vue/no-dupe-keys */
<template>
    <li>
        <label>
            <input type="checkbox" :checked='likeObj.finished' @change="IsChange"/>
            <span>{{likeObj.name}}</span>
        </label>
        <button class="btn btn-danger" @click="deleteLike">删除</button>
    </li>
</template>

<script>
    export default {
        name:'ItemInfo',
        props:['likeObj', 'IsSelect', 'isDelete'],
        methods: {
            IsChange(){
                this.IsSelect(this.likeObj.id)
            },
            deleteLike(){
                this.isDelete(this.likeObj.id)
            }
        },
    }
</script>

<style scoped>
/*item*/
li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}
li label {
  float: left;
  cursor: pointer;
}
li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}
li button {
  float: right;
  display: none;
  margin-top: 3px;
}
li:hover {
    background-color: #ddd;
}
li:hover button {
    display: block;
}
li:before {
  content: initial;
}
li:last-child {
  border-bottom: none;
}
</style>
  • FloorInfo.vue
<template>
    <div class="todo-footer" v-show="total">
        <label>
        <input type="checkbox" v-model='isCheck'/>
        </label>
        <span>
        <span>已完成{{finishNum}}</span> / 全部{{LikesList.length}}
        </span>
        <button class="btn btn-danger" @click="clearFinish">清除已完成任务</button>
    </div>
</template>

<script>
    export default {
        name:'FloorInfo',
        props:['LikesList', 'isCheckAll', 'isClear'],
        methods: {
            clearFinish(){
                this.isClear()
            }
        },
        computed:{
            total(){
                return this.LikesList.length 
            },
            finishNum(){
                return this.LikesList.reduce((num, like) => num + (like.finished?1:0), 0)
            },
            isCheck:{
                get(){
                    return this.total == this.finishNum 
                },
                set(value){
                    this.isCheckAll(value)
                }
            },
        }
    }
</script>

<style scoped>
/*footer*/
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}
.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}
.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}
.todo-footer button {
  float: right;
  margin-top: 5px;
}
</style>
  • App.vue
<template>
  <div id="app">
    <div class="todo-container">
      <div class="todo-wrap">
        <TopInfo :getLike="getLike"/>
        <ListInfo :LikesList='LikesList' :IsSelect='IsSelect' :isDelete='isDelete'/>
        <FloorInfo :LikesList='LikesList' :isCheckAll='isCheckAll' :isClear='isClear'/>
      </div>
    </div>
  </div>
</template>

<script>
import TopInfo from './components/TopInfo.vue'
import ListInfo from './components/ListInfo.vue'
import FloorInfo from './components/FloorInfo.vue'

export default {
  name: 'App',
  components: {
    TopInfo,
    ListInfo,
    FloorInfo
  },
  data() {
    return {
      LikesList:[
          {id:'001', name:'吃饭', finished:true},
          {id:'002', name:'睡觉', finished:false},
          {id:'003', name:'打豆豆', finished:false}
      ]
    }
  },
  methods: {
    getLike(obj){
      this.LikesList.unshift(obj)
    },
    IsSelect(id){
      this.LikesList.forEach((like)=>{
        if(id === like.id){
          like.finished = !like.finished
        }
      })
    },
    isDelete(id){
      this.LikesList = this.LikesList.filter(like => like.id!==id)
    },
    isCheckAll(isCheck){
      this.LikesList.forEach((like)=>{
        like.finished = isCheck
      })
    },
    isClear(){
      this.LikesList = this.LikesList.filter((like)=>{
        return like.finished == false
      })
    }
  },  
}
</script>

<style>
/*base*/
body {
  background:white;
}
.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}
.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}
.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}
.btn:focus {
  outline: none;
}
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>

2)总结

1.组件化编码流程:
	#(1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
	#(2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
		1).一个组件在用:放在组件自身即可。
		2). 一些组件在用:放在他们共同的父组件上(状态提升)。
	#(3).实现交互:从绑定事件开始。

2.props适用于:
	#(1).父组件 ==> 子组件 通信
	#(2).子组件 ==> 父组件 通信(要求父先给子一个函数)

3.使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!

4.props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。

3)本地存储

1.webStorage
	#存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
	#浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。

2.相关API:
	#xxxxxStorage.setItem('key', 'value');
	该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。

	#xxxxxStorage.getItem('person');
	该方法接受一个键名作为参数,返回键名对应的值。

	#xxxxxStorage.removeItem('key');
	该方法接受一个键名作为参数,并把该键名从存储中删除。

	#xxxxxStorage.clear()
	该方法会清空存储中的所有数据。

3.备注:
    #SessionStorage存储的内容会随着浏览器窗口关闭而消失。
    #LocalStorage存储的内容,需要手动清除才会消失。
    #xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。
    #JSON.parse(null)的结果依然是null
  • 代码
  data() {
    return {
      LikesList: JSON.parse(localStorage.getItem('likes')) || []	//获取本地数据,否则得到空数组
          // [{id:'001', name:'吃饭', finished:true},
          // {id:'002', name:'睡觉', finished:false},
          // {id:'003', name:'打豆豆', finished:false}]
      
    }
  },
  watch:{
    LikesList:{
      deep:true,	//深度监视(是否勾选)
      handler(value){
        localStorage.setItem('likes', JSON.stringify(value))	//保存
      }
    }
  },

3.4自定义事件-全局事件总线-消息订阅发布

1)自定义事件

  • App.vue
<template>
  <div id="app">
    <!-- 1.自定义事件 -->
    <!-- <Student @monkey='getName'/> -->
    <!-- 2.使用ref -->
    <Student ref='getName'/>
  </div>
</template>
<script>
import Student from './components/Student.vue'

export default {
  name: 'App',
  components: {
    Student
  },
  methods:{
    getName(name){
      alert(name)
    }
  },
  mounted() {         //方式二使用
    this.$refs.getName.$on('monkey',this.getName)
  },
}
</script>
  • Student.vue
<template>
  <div>
      <h2>姓名:{{name}}</h2>
      <h2>年纪:{{age}}</h2>
      <button @click="getNameInfo">点击获取名称</button>
      <button @click="death">销毁</button>
  </div>
</template>
<script>
export default {
    name:'Student',
    data() {
        return {
            name:'小张',
            age:18
        }
    },
    methods: {
        getNameInfo(){
            this.$emit('monkey', this.name)
        },
        death(){
            // this.$destroy('monkey')     //单个
            // this.$destroy(['monkey', 'monkey1'])     //多个
            this.$destroy()     //所有
        }
    },
}
</script>
  • 总结
1.一种组件间通信的方式,适用于:子组件 ===> 父组件

2.使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。

3.绑定自定义事件:
	第一种方式,在父组件中:<Demo @atguigu="test"/>或 <Demo v-on:atguigu="test"/>
	第二种方式,在父组件中:
        <Demo ref="demo"/>
        ......
        mounted(){
           this.$refs.xxx.$on('atguigu',this.test)
        }

4.若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

5.触发自定义事件:this.$emit('atguigu',数据)

6.解绑自定义事件this.$off('atguigu')

7.组件上也可以绑定原生DOM事件,需要使用native修饰符。

	注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!

2)全局事件总线

  • mian.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  beforeCreate(){
    Vue.prototype.$bus = this   //安装全局事件
  }
}).$mount('#app')
  • Student.vue
<template>
    <div class="demo">
        <h2>学生名称:{{name}}</h2>
        <h2>学生年龄:{{age+1}}</h2>
        <button @click="sendData">给school传数据</button>
    </div>
</template>
<script>
    export default {
        name:'Student',
        data(){
            return{
                 name:'张三',
                 age:18
            }
        },
        methods: {
            sendData(){
                this.$bus.$emit('data', this.age)	//触发事件
            }
        },
    }
</script>
  • School.vue
<template>
    <div class="demo">
        <h2>学校名称:{{name}}</h2>
        <h2>学校地址:{{addr}}</h2>
    </div>
</template>
<script>
    export default {
        name:'School',
        data(){
            return{
                name:'石沟子小学',
                addr:'西安'
            }
        },
        mounted() {
            this.$bus.$on('data', (data)=>{		//接收数据
                console.log('aaa',data)
            })
        },
        beforeDestroy(){
            this.$bus.$off('data')				//销毁全局事件
        }
    }
</script>
  • 总结
1.一种组件间通信的方式,适用于任意组件间通信。

2.安装全局事件总线:
    new Vue({
        ......
        beforeCreate() {
            Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
        },
        ......
    }) 

3.使用事件总线:
	(1)接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
        methods(){
            demo(data){......}
                      }
            ......
            mounted() {
                this.$bus.$on('xxxx',this.demo)
            }2)提供数据:this.$bus.$emit('xxxx',数据)

4.最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。

3)消息订阅发布

  • School.vue
<template>
    <div class="demo">
        <h2>学校名称:{{name}}</h2>
        <h2>学校地址:{{addr}}</h2>
    </div>
</template>
<script>
    import pubsub from 'pubsub-js'
    export default {
        name:'School',
        data(){
            return{
                name:'石沟子小学',
                addr:'西安'
            }
        },
        mounted() {
            this.pubId=pubsub.subscribe('hello', (name,data) => {
                console.log(name, data);
            })
        },
        beforeDestroy(){
            pubsub.unsubscribe(this.pubId)
        }
    }
</script>
  • Student.vue
<template>
    <div class="demo">
        <h2>学生名称:{{name}}</h2>
        <h2>学生年龄:{{age}}</h2>
        <button @click="sendData">给school传数据</button>
    </div>
</template>
<script>
    import pubsub from 'pubsub-js'
    export default {
        name:'Student',
        data(){
            return{
                 name:'张三',
                 age:18
            }
        },
        methods: {
            sendData(){
                pubsub.publish('hello', this.age)
            }
        },
    }
</script>
  • 总结
1.一种组件间通信的方式,适用于任意组件间通信。

2.使用步骤:
	安装pubsub:npm i pubsub-js
	引入: import pubsub from 'pubsub-js'

3.接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
    methods(){
      demo(data){......}
    }
    ......
    mounted() {
      this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
    }

4.提供数据:pubsub.publish('xxx',数据)

5.最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。

4)小知识点

1.nextTick
1.语法:this.$nextTick(回调函数)
2.作用:在下一次 DOM 更新结束后执行其指定的回调。
3.什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
插槽slot
2.过渡动画
<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
		<transition-group name="hello" appear>
			<h1 v-show="!isShow" key="1">你好啊!</h1>
			<h1 v-show="isShow" key="2">尚硅谷!</h1>
		</transition-group>
	</div>
</template>

<script>
	export default {
		name:'Test',
		data() {
			return {
				isShow:true
			}
		},
	}
</script>

<style scoped>
	h1{
		background-color: orange;
	}
	/* 进入的起点、离开的终点 */
	.hello-enter,.hello-leave-to{
		transform: translateX(-100%);
	}
	.hello-enter-active,.hello-leave-active{
		transition: 0.5s linear;
	}
	/* 进入的终点、离开的起点 */
	.hello-enter-to,.hello-leave{
		transform: translateX(0);
	}

</style>
3.第三方动画库

https://animate.style/

<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
		<transition-group 
			appear
			name="animate__animated animate__bounce" 
			enter-active-class="animate__swing"
			leave-active-class="animate__backOutUp"
		>
			<h1 v-show="!isShow" key="1">你好啊!</h1>
			<h1 v-show="isShow" key="2">尚硅谷!</h1>
		</transition-group>
	</div>
</template>
<script>
	import 'animate.css'
	export default {
		name:'Test',
		data() {
			return {
				isShow:true
			}
		},
	}
</script>
<style scoped>
	h1{
		background-color: orange;
	}
</style>

3.5 axios

1)代理两种方式

方式1
  • vue.config.js
module.exports = {
    devServer: {
      proxy: 'http://localhost:1234',	//代理地址
      port: 8081 	//修改前端端口
    }
  }
//说明:

	//1.优点:配置简单,请求资源时直接发给前端(8080)即可。
	//2.缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
	//3.工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
  • 使用
<template>
    <div>
        <button @click="test">测试</button>
    </div>
</template>
<script>
    import axios from 'axios'		//1.导包
    export default {
        name:'App',
        methods: {
            test(){					//2.使用
                axios.get('http://localhost:8080/test?id=1').then(	//本来地址
                    (response)=>{
                        console.log(response.data)
                        console.log(response)
                    },
                )
            }
    }
</script>
方式2
  • 配置
module.exports = {
    devServer: {
    proxy: {
        '/test': {                              //注意:必须后端地址带有/test
          target: 'http://localhost:1234',      //后端地址
          ws: true,     //支持websoket
          changeOrigin: true,   //默认,用于请求头中host(true:后端地址,false:前端地址)
          pathRewrite:{         //将路径中api替换掉
              '^/test':'/test'
          }
        }
      }
    }
  }
//说明:

	//1.优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
	//2.缺点:配置略微繁琐,请求资源时必须加前缀。

2)get/post请求使用

get
//方式一
data(){
    return{
        id:1
    }
}

axios.get(`/test?id=${this.id}`).then(		//注意:拼接参数必须用``不是单引号''
    (response)=>{
        console.log(response.data)
    },
    error=>{
        console.log('err', error.massage)
    }
)
//方式二
axios.get('/test',{					//以参数形式传进来
    params:{
        id : 3
    }
}).then(
    (response)=>{
        console.log(response.data)
    },
    error=>{
        console.log('err', error.massage)
    }
)
post
data() {
    return {
        post:{
            name: 'aaa',
            addr: 'bbb'
        }
    }
}

axios.post('/test1', this.post).then(
    (response)=>{
        console.log(response.data)
    },
    error=>{
        console.log('err', error.massage)
    }
)

3)GitHub案例

  • 公共样式
1.目录 pubulic--css--bootstrap.css
2.在index.html引入
	<!-- 引入静态资源 -->
    <link rel="myStype" href="<%= BASE_URL %>css/bootstrap.css">
  • Header.vue
<template>
    <div>
        <section class="jumbotron">
            <h3 class="jumbotron-heading">Search Github Users</h3>
            <div>
            <input type="text" placeholder="enter the name you search" v-model="keyword" @keyup.enter='getInfo'/>&nbsp;
            <button @click="getInfo">Search</button>
            </div>
        </section>    
    </div>    
</template>
<script>
import axios from 'axios'
export default {
    name:'Header',
    data() {
        return {
            keyword:'' ,
        }
    },
    methods: {
        getInfo(){
            this.$bus.$emit('userList',{isFirst:false,isLoad:true,errMsg:'', users:[]})
            axios.get(`https://api.github.com/search/users?q=${this.keyword}`).then(
            response=>{
                this.$bus.$emit('userList',{isLoad:false,errMsg:'', users:response.data.items})
            },
            error=>{
                this.$bus.$emit('userList',{isLoad:false,errMsg: error.message, users:[]})
            })
        }
    },
}
</script>
  • List.vue
<template>
    <div class="row">
      <div v-show="info.users.length" class="card" v-for="user in info.users" :key="user.login">
        <a :href="user.html_url" target="_blank">
          <img :src="user.avatar_url" style='width: 100px'/>
        </a>
        <p class="card-text">{{user.login}}}</p>
      </div>
    <h1 v-show="info.isFirst">欢迎使用!!!</h1>
    <h1 v-show="info.isLoad">Loading。。。</h1>
    <h1 v-show="info.errMsg">{{info.errMsg}}</h1>
    </div>   
</template>
<script>
export default {
    data() {
        return {
            info:{
                isFirst:true,
                isLoad:false,
                errMsg:'',
                users:[]
            }
        }
    },
    mounted(){
        this.$bus.$on('userList', (users)=>{
            this.info = {...this.info, ...users}	//比较放进info
        })
    }
}
</script>
<style>
   .album {
      min-height: 50rem; /* Can be removed; just added for demo purposes */
      padding-top: 3rem;
      padding-bottom: 3rem;
      background-color: #f7f7f7;
    }
    .card {
      float: left;
      width: 33.333%;
      padding: .75rem;
      margin-bottom: 2rem;
      border: 1px solid #efefef;
      text-align: center;
    }
    .card > img {
      margin-bottom: .75rem;
      border-radius: 100px;
    }
    .card-text {
      font-size: 85%;
    }
</style>
  • App.vue
<template>
    <div class="container">
        <Header/>
        <List/>
    </div>
</template>
<script>
    import Header from "./components/Header.vue";
    import List from "./components/List.vue";
    export default{
        name:'App', 
        components:{
            Header,
            List
        },
    }
</script>

4)vue-resource请求数据

1.安装
	npm install vue-resource
2.main.js引入
	import vueResource form 'vue-resource'
	Vue.use(vueResource)
3.使用(和axios相似)
	将axios.get(url)替换为this.$http.get(url)

注意:
	//维护比较少,axios好一点

5)插槽Solt

默认卡槽
  • App.vue
<template>
<div class="container"> 
    <Category title="美食">
        <img src="https://img2.baidu.com/it/u=1476158892,3155909481&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=281" alt="">
    </Category>
    <Category title="游戏">
        <ul>
            <li v-for="(game,index) in games" :key="index">{{game}}</li>
        </ul>
    </Category>
    <Category title="电影">
        <video src="https://www.bilibili.com/video/BV1Zy4y1K7SH/"></video>
    </Category>
</div>
</template>
<script>
    import Category from './components/Category.vue'
    export default{
        name:'App', 
        components:{
            Category
        },
        data() {
			return {
				foods:['火锅','烧烤','小龙虾','牛排'],
				games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
				films:['《教父》','《拆弹专家》','《你好,李焕英》','《尚硅谷》']
			}
		},
    }
</script>
<style>
    .container{
        display: flex;
        justify-content: space-around;
    }
    h3{
        text-align: center;
        background-color: orange;
    }
    img{
        width: 100%;
    }
    video{
        width: 100%;
    } 
</style>
  • Category.vue

    <template>
        <div class="category">
            <h3>{{title}}分类</h3>
            <slot></slot>		//相当于填充
        </div>	
    </template>
    <script>
    export default {
        name:'Category',
        props:['title']
    }
    </script>
    <style>
        .category{
            background-color: skyblue;
            width: 200px;
            height: 300p;
        }
    </style>
    
具名卡槽
1.app.vue
    <Category title="美食">
        <img slot="image" src="https://img2.baidu.com/it/u=1476158892,3155909481&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=281" alt="">
        <a slot="dump" href="http://ww.baidu.com">百度</a>
    </Category>

2.category.vue
    <slot name="image"></slot>
    <slot name="dump"></slot>
作用域卡槽
1.父组件
	<Category title="游戏">
        <template scope="data">
          <ul>
            <li v-for="(game,index) in data.games" :key="index">{{game}}</li>
           </ul>
        </template>
    </Category>
2.子组件
	<slot :games="games"></slot>

	games:['红色警戒','穿越火线','劲舞团','超级玛丽'],

3.6 vuex

1)介绍

1.对vue 应用中多个组件的共享状态进行集中式的管理(读/写)
	适用于单页面组件之间数据交换比较频繁
	用于任意组件间通信

2)

3.7 路由

1)介绍

vue-router理解
vue的一个插件库,专门来实现SPA应用
SPA理解
1.单页面应用,整个应用只有一个完整的页面
2.点击页面导航连接不会刷新页面,指挥做页面的局部更新
3.数据通过Ajax获取请求
路由
1.一组映射关系(key-value)
2.key为路径。value可能为function或component
路由分类
1.后端路由
	1)value是function,用于处理客户端提交的请求
	2)过程:服务器收到请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据

2.前端路由
	1)value是component,用于展示页面内容
	2)过程:当浏览器路径改变时,对应的组件就会显示

2)基本使用

安装
1.npm默认安装是vue-router 4版本
	vue2对应的vue-router版本是3		#安装命令(npm i vue-router@3)	
代码
  • 引入bootstrap.css文件
1.在public--css下存放bootstrap.css文件

2.在index.html引入css
	<!-- 引入静态资源 -->
    <link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">
  • 在router文件夹创建index.js
import VueRouter from 'vue-router'
import Vue from 'vue'
import About from '../components/About.vue'
import Home from '../components/Home.vue'

Vue.use(VueRouter)

export default new VueRouter({
    routes: [
        {
            path:'/about',
            component: About
        },
        {
            path:'/home',
            component: Home
        }
    ]   
})
  • main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router/index'   

Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  router
}).$mount('#app')
  • App.vue
<template>
   <div>
    <div class="row">
      <div class="col-xs-offset-2 col-xs-8">
        <div class="page-header"><h2>Vue Router Demo</h2></div>
      </div>
    </div>
    <div class="row">
      <div class="col-xs-2 col-xs-offset-2">
        <div class="list-group">
            <!-- 使用路由-->
          <router-link class="list-group-item" active-class='active' to="/about">About</router-link>
          <router-link class="list-group-item" active-class='active' to="/home">Home</router-link>
        </div>
      </div>
      <div class="col-xs-6">
        <div class="panel">
          <div class="panel-body">
               <!-- <h2>我是About的内容</h2> -->
               <router-view msg='aaa'></router-view>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
    export default{
        name:'App', 
    }
</script>
  • About.vue
<template>
    <div>
        <h2>我是About的内容</h2>
    </div>
</template>
  • Home.vue
<template>
    <div>
        <h2>我是Home的内容</h2>
    </div>
</template>

3)路由嵌套

  • 配置
routes:{
    path:'/home',
    component: Home,
    children:[
        {
            path:'news',		// 路径注意不能加'/'
            component: News,
        },
    ]
}
  • 使用
<router-link to="/home/news">News</router-link>

4)query参数传递

  • 方式一(拼接参数)
<router-link :to="`/home/message/detail?id=${info.id}&name=${info.name}`">{{info.name}}</router-link>
  • 方式二(对象式)
<router-link :to="{
                  path:'/home/message/detail',
                  query:{
                  	id:info.id,
                  	name:info.name
                  }}">
    {{info.name}}
</router-link>
  • 使用
$route.query

5)params参数传递

  • 命名路由
{
	name:'setName',
	path:'detail',
	component: Detail,
}
<!--简化前,需要写完整的路径 -->
<router-link :to="`/home/message/detail?id=${info.id}&name=${info.name}`">{{info.name}}</router-link>


<!--简化后,直接通过名字跳转 -->
<router-link :to="`setName?id=${info.id}&name=${info.name}`">{{info.name}}</router-link>
                   

<!--简化写法配合传递参数 -->
<router-link 
     :to="{
          name:'hello',
          query:{
          	id:666,
          	name:'你好'
          }
     }"
>跳转</router-link>
  • params参数
<!-- 第一种-->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>

<!-- 第二种-->
<router-link :to="{
                  name:'setName',       //注意:只能是name
                  params:{
                  	id:info.id,
                  	name:info.name
                  }
             }">
    {{info.name}}
</router-link>
  • 使用
$route.params

6)props配置

  • 配置
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{
//     id: 1,
//     name:"小藏"
// }

//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
// props:true

//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props($route){
    return{
        id:$route.params.id,
        name:$route.params.name,
    }
}
  • 使用
<template >
    <div>
        <ul>
            <li>{{id}}</li>
            <li>{{name}}</li>
        </ul>
    </div>
</template>
<script>
export default {
    props:['id','name']
}
</script>

7)replace属性

1.作用:控制路由跳转时操作浏览器历史记录的模式

2.浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push

3.如何开启replace模式:<router-link replace .......>News</router-link>

8)响应式编程

//$router的两个API,不借助reouter-link
//1.点击按钮,显示子路由。push是追加历史记录
this.$router.push({
    name:'xiangqing',
    params:{
        id:xxx,
        title:xxx
    }
})
//2.点击按钮,显示子路由。replace是替换当前记录
this.$router.replace({
    name:'xiangqing',
    params:{
        id:xxx,
        title:xxx
    }
})

//使用
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go(2) //可前进也可后退(例:2.前进2步;-3.后退3步)

9)缓存路由组件

<!--输入文本框数据,切换路由,返回时数据没有被销毁。不写include默认全部,News表示组件名-->
<keep-alive include="News"> 
    <router-view></router-view>
</keep-alive>

注意:
	多个缓存路由使用 :include="['News','Message']"

10)两个新的生命周期钩子

1.作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
2.具体名字:
	activated路由组件被激活时触发。
	deactivated路由组件失活时触发。

11)路由守卫

  • 前置守卫&后置路由
//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
    console.log('beforeEach',to,from)
    if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
        if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
            next() //放行
        }else{
            alert('暂无权限查看')
            // next({name:'guanyu'})
        }
    }else{
        next() //放行
    }
})

//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
    console.log('afterEach',to,from)
    if(to.meta.title){ 
        document.title = to.meta.title //修改网页的title
    }else{
        document.title = 'vue_test'
    }
})
  • 独享路由守卫
beforeEnter(to,from,next){
    console.log('beforeEnter',to,from)
    if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
        if(localStorage.getItem('school') === 'atguigu'){
            next()
        }else{
            alert('暂无权限查看')
            // next({name:'guanyu'})
        }
    }else{
        next()
    }
}
  • 组件内路由守卫
   //进入守卫:通过路由规则,进入该组件时被调用
   beforeRouteEnter (to, from, next) {
   },
   //离开守卫:通过路由规则,离开该组件时被调用
   beforeRouteLeave (to, from, next) {
   }

12)路由的两种工作模式

1.对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
2.hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
	#hash模式:
        地址中永远带着#号,不美观 。
        若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
        兼容性较好。
	#history模式:
        地址干净,美观 。
        兼容性和hash模式相比略差。
        应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
3.路由配置
	mode:'history',

3.8 elment-ui

1)导入 Elment-UI 相关资源

//安装
npm i element-ui
//导入组件库
import ElmentUI from 'element-ui';
//导入组件相关样式
import 'element-ui/lib/theme-chalk/index.css';

//配置Vue插件
Vue.use(ElementUI);

2)使用Mint UI

https://blog.csdn.net/weixin_44972008/article/details/113921339

补充:

1.快捷键

  • :生成html模板 (!+ enter)

  • :生成vue实例**(newVue)**

  • :生成vue模板**(vueInit)**

  • ()

  • ()

  • ()

  • ()

  • ()

2.插件

  • :Vue提示插件**(Vue 3 Snippets)**
  • :右键直接浏览器打开**(Live Server)**
  • :Vue文件高亮显示**(Vetur)**
  • :语法纠错**(EsLint )**
  • :ES6语法智能提示以及快速输入**(JavaScript(ES6) code snippets)**
  • :css样式提示**(IntelliSense for CSS class names)**
  • ()
  • ()
  • ()

ript>


### 7)replace属性

```vue
1.作用:控制路由跳转时操作浏览器历史记录的模式

2.浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push

3.如何开启replace模式:<router-link replace .......>News</router-link>

8)响应式编程

//$router的两个API,不借助reouter-link
//1.点击按钮,显示子路由。push是追加历史记录
this.$router.push({
    name:'xiangqing',
    params:{
        id:xxx,
        title:xxx
    }
})
//2.点击按钮,显示子路由。replace是替换当前记录
this.$router.replace({
    name:'xiangqing',
    params:{
        id:xxx,
        title:xxx
    }
})

//使用
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go(2) //可前进也可后退(例:2.前进2步;-3.后退3步)

9)缓存路由组件

<!--输入文本框数据,切换路由,返回时数据没有被销毁。不写include默认全部,News表示组件名-->
<keep-alive include="News"> 
    <router-view></router-view>
</keep-alive>

注意:
	多个缓存路由使用 :include="['News','Message']"

10)两个新的生命周期钩子

1.作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
2.具体名字:
	activated路由组件被激活时触发。
	deactivated路由组件失活时触发。

11)路由守卫

  • 前置守卫&后置路由
//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
    console.log('beforeEach',to,from)
    if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
        if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
            next() //放行
        }else{
            alert('暂无权限查看')
            // next({name:'guanyu'})
        }
    }else{
        next() //放行
    }
})

//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
    console.log('afterEach',to,from)
    if(to.meta.title){ 
        document.title = to.meta.title //修改网页的title
    }else{
        document.title = 'vue_test'
    }
})
  • 独享路由守卫
beforeEnter(to,from,next){
    console.log('beforeEnter',to,from)
    if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
        if(localStorage.getItem('school') === 'atguigu'){
            next()
        }else{
            alert('暂无权限查看')
            // next({name:'guanyu'})
        }
    }else{
        next()
    }
}
  • 组件内路由守卫
   //进入守卫:通过路由规则,进入该组件时被调用
   beforeRouteEnter (to, from, next) {
   },
   //离开守卫:通过路由规则,离开该组件时被调用
   beforeRouteLeave (to, from, next) {
   }

12)路由的两种工作模式

1.对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
2.hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
	#hash模式:
        地址中永远带着#号,不美观 。
        若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
        兼容性较好。
	#history模式:
        地址干净,美观 。
        兼容性和hash模式相比略差。
        应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
3.路由配置
	mode:'history',

3.8 elment-ui

1)导入 Elment-UI 相关资源

//安装
npm i element-ui
//导入组件库
import ElmentUI from 'element-ui';
//导入组件相关样式
import 'element-ui/lib/theme-chalk/index.css';

//配置Vue插件
Vue.use(ElementUI);

2)使用Mint UI

https://blog.csdn.net/weixin_44972008/article/details/113921339

补充:

1.快捷键

  • :生成html模板 (!+ enter)

  • :生成vue实例**(newVue)**

  • :生成vue模板**(vueInit)**

  • ()

  • ()

  • ()

  • ()

  • ()

2.插件

  • :Vue提示插件**(Vue 3 Snippets)**
  • :右键直接浏览器打开**(Live Server)**
  • :Vue文件高亮显示**(Vetur)**
  • :语法纠错**(EsLint )**
  • :ES6语法智能提示以及快速输入**(JavaScript(ES6) code snippets)**
  • :css样式提示**(IntelliSense for CSS class names)**
  • ()
  • ()
  • ()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值