Ming Ming
首页
  • VUE2
  • VUE3
  • 设计模式
  • JUC
  • Nginx
  • RabbitMQ
  • Redis
  • linux
  • SQL
  • MyBatis
  • 软件使用
  • 工具类
  • 在线工具

    • githubHosts (opens new window)
    • 正则表达式 (opens new window)
    • 文件转换器 (opens new window)
    • 代码在线运行 (opens new window)
GitHub (opens new window)
首页
  • VUE2
  • VUE3
  • 设计模式
  • JUC
  • Nginx
  • RabbitMQ
  • Redis
  • linux
  • SQL
  • MyBatis
  • 软件使用
  • 工具类
  • 在线工具

    • githubHosts (opens new window)
    • 正则表达式 (opens new window)
    • 文件转换器 (opens new window)
    • 代码在线运行 (opens new window)
GitHub (opens new window)
  • 前端

    • vue_base
      • 一、Vue核心
        • 1. 初识 Vue
        • 2. 模板语法
        • 3. 数据代理
        • 4. 事件处理
        • 1. 绑定监听
        • 2. 事件修饰符
        • 3. 按键修饰符
        • 5. 属性监听与计算
        • 6. 数据监视原理
        • 7. 列表渲染
        • 1. 列表显示
        • 2. key原理
        • 8. Vue指令
        • 1.常用内置指令
        • 2. 自定义指令
        • 9. 生命周期
        • 1. 定义
        • 2. 周期类别
        • 3. 执行情况
      • 二、Vue组件化编程
        • 1. 非单文件组件
        • 1. 组件创建
        • 2.组件嵌套
        • 3. 组件本质
        • 4. 内置关系
        • 2. 单文件组件
        • 3. 相关小知识
        • 1. ref标签属性
        • 2. props配置项
        • 3. mixin配置项
        • 4. 插件
        • 5. scoped-样式
        • 6. nextTick
        • 7. slot-插槽
        • 4. 自定义事件
        • 5. 全局事件总线
      • 三、Vue脚手架使用
        • 1. 安装
        • 2. 创建一个项目
        • 3. 组件化编码流程
        • 4. 配置代理
      • 四、Vuex
        • 1. 四个map方法的使用
        • 2. 模块化+命名空间
      • 五、Vue Router
        • 1. 基本使用
        • 2. 几个注意点
        • 3. 嵌套路由
        • 4. 路由的query参数
        • 5. 命名路由
        • 6. 路由的params参数
        • 7.路由的props配置
        • 8. replace属性
        • 9. 编程式路由导航
        • 10. 缓存路由组件
        • 11. 路由守卫
        • 全局守卫
        • 独享守卫
        • 组件内守卫
    • vue3_base
  • 前端
  • 前端
J-Ming
2021-11-08
目录

vue_base

# 一、Vue核心

# 1. 初识 Vue

① 要用vue必须拥有一个vue实例且需要传入配置对象

② html中容器需要与一个vue实例绑定且为一 一对应关系,容器中的代码为【vue模板】

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>初始vue</title>
    // 引入vue
    <script src="./js/vue.js"></script>
</head>

<body>
    <div id="root">
        <h1>hello,{{name}}</h1>
    </div>
    <script>
        Vue.config.productionTip = false;
         // 创建vue实例
        let vueConf = {
            // 绑定对应容器
            el: "#root",
            data() {
                return {
                    name: "ming"
                }
            },
        }; 
        const vm = new Vue(vueConf);
    </script>
</body>
</html>

# 2. 模板语法

html 中包含了一些 JS 语法代码,语法分为两种,分别为:

  1. 插值语法(双大括号表达式)
  2. 指令(以 v-开头)

插值语法:

  1. 功能: 用于解析标签体内容
  2. 语法: ,xxxx 会作为 js 表达式解析

指令语法:

  1. 功能: 解析标签属性、解析标签体内容、绑定事件
  2. 举例:v-bind:href = 'xxxx' ,xxxx 会作为 js 表达式被解析
  3. 说明:Vue 中有有很多的指令,此处只是用 v-bind 举个例子
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>初始vue</title>
    <script src="./js/vue.js"></script>
</head>

<body>
    <div id="root">
        <h1>插值语法:</h1>
        <h2>hello,{{name}}</h2>
        <hr/>
        <h1>指令语法:</h1>
        <a :href="url">百度</a>
    </div>
    <script>
        Vue.config.productionTip = false;
        // 创建vue实例
        let vueConf = {
            // 绑定对应容器
            el: "#root",
            data() {
                return {
                    name: "ming",
                    url: "https://www.baidu.com"
                }
            },
        }; 
        const vm = new Vue(vueConf);
    </script>
</body>
</html>

# 3. 数据代理

<script>
    // 被代理对象
    let obj = {
        name: "ming",
        url: "https://www.baidu.com",
    };
    // 代理对象
    let age = 12;

    // 数据代理
    Object.defineProperty(obj, "age", {
        get() {
            console.log("obj.age进行读取")
            return age;
        },
        set(value) {
            console.log("obj.age进行写入")
            age = value;
        }
    });
</script>

# 4. 事件处理

# 1. 绑定监听

  1. v-on:xxx="fun"
  2. @xxx="fun"
  3. @xxx="fun(参数)"
  4. 默认事件形参: event
  5. 隐含属性对象: $event

# 2. 事件修饰符

例:@click.stop.prevent(按顺序执行)

  • .stop:等同于JavaScript中的event.stopPropagation(),防止事件冒泡
  • .prevent:等同于JavaScript中的event.preventDefault(),防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播)
  • .capture:与事件冒泡的方向相反,事件捕获由外到内
  • .self:只会触发自己范围内的事件,不包含子元素
  • .once:只会触发一次

# 3. 按键修饰符

在JavaScript事件中除了前面所说的事件,还有键盘事件,也经常需要监测常见的键值。在Vue中允许v-on在监听键盘事件时添加关键修饰符。记住所有的keyCode比较困难,所以Vue为最常用的键盘事件提供了别名:

例:@keyup.enter

键盘:

  • .enter:回车键
  • .tab:制表键
  • .delete:含delete和backspace键
  • .esc:返回键
  • .space: 空格键
  • .up:向上键
  • .down:向下键
  • .left:向左键
  • .right:向右键

鼠标:

  • .left:鼠标左键
  • .middle:鼠标中间滚轮
  • .right:鼠标右键

# 5. 属性监听与计算

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>天气案例_监视属性</title>
    <script src="./js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h2>现在季节为{{weather}}</h2>
        <button @click="changeWeather()">切换季节</button>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: "#root",
            data() {
                return {
                    status: true
                }
            },
            // 计算
            computed: {
                /* weather: {
                    // 调用weather或者用来计算的属性status变化时执行
                    get() {
                        console.log("weather被使用了")
                        return this.status ? '春天' : '冬天';
                    },
                    set(value) {
                        console.log("weather被修改了")
                        this.status = value;
                    }

                }, */
                // 简写不包含set()
                weather() {
                    return this.status ? '春天' : '冬天';
                }
            },
            // 监听
            watch: {
                weather: {
                    // 是否马上执行
                    //immediate: true,
                    // 开启深度监视,VUE在watch中默认不监视对象里面的值的改变
                    //deep: true,
                    handler(newValue, oldValue) {
                        console.log("季节变化了", oldValue + "-->" + newValue)
                    }
                }
            },
            methods: {
                changeWeather() {
                    this.status = !this.status;
                }
            },
        })
    </script>
</body>
</html>

# 6. 数据监视原理

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

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

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

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

<script>
  let data = {
            name: 'a',
            age: 1,
            person: {
                name: "chs"
            }
        }

        // 用来监视data属性变化
        function Observer(obj) {
            // 获取data中所有属性
            const keys = Object.keys(obj);
            keys.forEach(key => {
                // 属性值
                let val = data[key];
                Object.defineProperty(this, key, {
                    get() {
                        console.log(`data中${key}被读取了,值为${val}`)
                        return val;
                    },
                    set(newVal) {
                        console.log(`data中${key}被修改了从${val}-->${newVal}`)
                        val = newVal;
                    }
                })
            })
        }
        const obj = new Observer(data);
        let vm = {};
        vm._data = data = obj;
</script>

# 7. 列表渲染

# 1. 列表显示

v-for:

用于展示列表数据

  • 语法:v-for="(item, index) in xxx" :key="yyy"
  • 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
  • 数组: (item, index)
  • 对象: (value, key)
  • 字符串:(char, index)
  • 数字:(number, index)
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>基本列表</title>
    <script src="./js/vue.js"></script>
    <style>
        * {
            margin-top: 20px;
        }
    </style>
</head>

<body>
    <div id="root">
        <input type="text" name="" v-model="keyWords">
        <button @click="sortType = 0">原顺序</button>
        <button @click="sortType = 1">年龄升序</button>
        <button @click="sortType = 2">年龄降序</button>
        <ul>
            <li v-for="(item, index) in filPerson" :key="item.id">
                {{item.name}} - {{item.age}}
            </li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: "#root",
            data() {
                return {
                    keyWords: "",
                    // 排序类型 0:原顺序,1:升序,2:降序
                    sortType: 0,
                    person: [
                        { id: "01", name: "张三", age: 31 },
                        { id: "02", name: "王二", age: 22 },
                        { id: "03", name: "李二", age: 13 },
                        { id: "04", name: "张飞", age: 45 },
                        { id: "05", name: "许飞", age: 23 },
                    ]
                }
            },
            computed: {

                filPerson() {
                    // 人员过滤
                    const arr = this.person.filter((item) => {
                        return item.name.indexOf(this.keyWords) !== -1;
                    })

                    // 年龄排序
                    if (this.sortType) {
                        arr.sort((a, b) => {
                            return this.sortType === 1 ? a.age - b.age : b.age - a.age;
                        })
                    }
                    return arr;
                }
            },
        })
    </script>
</body>

</html>

# 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是没有问题的。

# 8. Vue指令

# 1.常用内置指令

  • v-text: 更新元素的 textContent

  • v-html: 更新元素的 innerHTML

  • v-if: 如果为true, 当前标签才会输出到页面

  • v-else: 如果为false, 当前标签才会输出到页面

  • v-show : 通过控制display样式来控制显示/隐藏

  • v-for : 遍历数组/对象

  • v-on: 绑定事件监听, 一般简写为@

  • v-bind : 强制绑定解析表达式, 可以省略v-bind

  • v-model: 双向数据绑定

  • v-text : 更新元素的 textContent

    ​ 作用:向其所在的节点中渲染文本内容。 ​ 与插值语法的区别:v-text会替换掉节点中的内容,则不会

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

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

  • **v-pre:**跳过其所在节点的编译过程。

  • ref : 为某个元素注册一个唯一标识, vue对象通过$refs属性访问这个元素对象

  • v-cloak : 使用它防止闪现表达式, 与css配合: [v-cloak] { display: none }

    ​ 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。 ​ 使用css配合v-cloak可以解决网速慢时页面展示出的问题

# 2. 自定义指令

自定义指令directives中的this为windows并不是Vue

<script>
        // 局部指令
        new Vue({
            el: '#root',
            data: {},
            directives: {
                // 指令名: v-fbind 如果指令名有多个单词则使用'-'连接不能使用驼峰形式 例:fbind-text-or-number
                'fbind': {
                    // 指令与元素成功绑定时调用
                    bind(element, binding) {
                        element.value = binding.value
                    },
                    // 指令所在元素被插入页面时调用
                    inserted(element, binding) {
                        element.focus()
                    },
                    // 指令所在的模板被重新解析时调用
                    update(element, binding) {
                        element.value = binding.value
                    }
                }
            }
        })

        // 全局指令
        Vue.directive('fbind', {
            // 指令与元素成功绑定时
            bind(element, binding) {
                element.value = binding.value
            },
            // 指令所在元素被插入页面时
            inserted(element, binding) {
                element.focus()
            },
            // 指令所在的模板被重新解析时
            update(element, binding) {
                element.value = binding.value
            }
        })
</script>

# 9. 生命周期

# 1. 定义

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

# 2. 周期类别

**beforeCreate😗*初始化数据监测和数据代理之前调用

此时data中的数据和methods中的方法不能访问

**created😗*初始化数据监测和数据代理之后调用

此时data中的数据和methods中的方法可以访问,在created后beforeMount之前会开始解析Vue模板生成虚拟DOM

**beforeMount😗*挂载完成之前

此时Vue模板已经被解析且生成了虚拟DOM所以此时修改DOM是无效的

**mounted😗*挂载完成后

Vue完成模板解析并把初始的真实DOM中的元素放入页面后(挂载完毕)调用mounted()

**beforeUpdate😗*页面更新之前

此时model已经被更新了但是view还没有更新,在更新前到更新后之间会生成新的虚拟DOM然后与旧的DOM进行diff并对view进行渲染

**updated😗*页面更新后

此时页面与数据都更新完成

beforeDestroy: 销毁之前

**destroyed😗*销毁后

**activated😗*路由组件被激活时触发

**deactivated😗*路由组件失活时触发

# 3. 执行情况

  1. 在页面一加载就会调用beforeCreate --> created --> beforeMount --> mounted此时会完成第一次的模板解析并进行view的渲染。
  2. 此后每更新一次data都会调用beforeCreate和created完成model对view的更新
Vue生命周期

# 二、Vue组件化编程

# 1. 非单文件组件

步骤:

  1. 定义组件(创建组件)
  2. 注册组件
  3. 使用组件(写组件标签)

组件命名:

  • kebab-case命名: my-school
  • CamelCase命名: MySchool

# 1. 组件创建

<html>
<head>
    <meta charset="UTF-8" />
    <title>组件使用</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <!-- 准备好一个容器-->
    <div id="root">
        <hello></hello>
        <hr>
        <h1>{{msg}}</h1>
        <hr>
        <!-- 第三步:编写组件标签 -->
        <school></school>
        <hr>
        <!-- 第三步:编写组件标签 -->
        <student></student>
    </div>

    <div id="root2">
        <hello></hello>
    </div>
</body>

<script type="text/javascript">
    Vue.config.productionTip = false

    // 创建school组件 Vue.extend({})与new Vue({})里面配置对象基本类似
    const school = Vue.extend({
        // 不需要绑定el:"",因为组件可能要为多个容器服务
        template: `
				<div class="demo">
					<h2>学校名称:{{schoolName}}</h2>
					<h2>学校地址:{{address}}</h2>
					<button @click="showName">点我提示学校名</button>	
				</div>
			`,
        // 数据对象必须为函数返回的对象,防止不同容器数据对象指向同一地址
        data() {
            return {
                schoolName: '尚硅谷',
                address: '北京昌平'
            }
        },
        methods: {
            showName() {
                alert(this.schoolName)
            }
        },
    })

    const student = Vue.extend({
        template: `
				<div>
					<h2>学生姓名:{{studentName}}</h2>
					<h2>学生年龄:{{age}}</h2>
				</div>
			`,
        data() {
            return {
                studentName: '张三',
                age: 18
            }
        }
    })

    // 创建hello组件
    const hello = Vue.extend({
        template: `
				<div>	
					<h2>你好啊!{{name}}</h2>
				</div>
			`,
        data() {
            return {
                name: 'Tom'
            }
        }
    })

    //创建vm
    new Vue({
        el: '#root',
        data: {
            msg: '你好啊!'
        },
        // 局部注册组件
        components: {
            school,
            student,
            hello
        }
    })
	// 全局注册组件
    Vue.component('hello', hello)
    new Vue({
        el: '#root2',
    })
</script>
</html>

# 2.组件嵌套

<script type="text/javascript">
    Vue.config.productionTip = false
    // 1.创建student组件作为子组件
    const student = Vue.extend({
        template: `
				<div>
					<h2>学生姓名:{{studentName}}</h2>
					<h2>学生年龄:{{age}}</h2>
				</div>
			`,
        data() {
            return {
                studentName: '小明',
                age: 18
            }
        }
    })

    // 2.创建school组件作为父组件
    const school = Vue.extend({
        template: `
				<div>
					<h2>学校名称:{{schoolName}}</h2>
                    <h2>学校地址:{{address}}</h2>
                    <student></student>
				</div>
			`,
        data() {
            return {
                schoolName: '希望小学',
                address: '中国'
            }
        },
        // 组件中嵌套子组件
        components: {
            student
        }
    })

    // 创建hello组件
    const hello = Vue.extend({
        template: `
				<div>	
					<h2>你好啊!{{name}}</h2>
				</div>
			`,
        data() {
            return {
                name: '小明'
            }
        }
    })

    // 3.创建一个app组件来管理所有组件
    const app = Vue.extend({
        template: `
                <div>
                    <school></school>
                    <hello></hello>
                </div>
                `,
        components: {
            school,
            hello
        }
    })

    new Vue({
        template:`<app></app>`,
        el: '#root',
        data: {
            msg: '你好啊!'
        },
        // 注册组件(局部注册)
        components: {
            app
        }
    })
</script>

# 3. 组件本质

  1. 组件实质是一个VueComponent构造函数
  2. 使用Vue.extend(options)创建组件时Vue会定义一个VueComponent并返回,所以每个VueComponent都是不同的
  3. Vue解析模板使用组件时会new VueComponent(options)
Vue.extend = function (extendOptions) {
	......
      var Sub = function VueComponent (options) {
        this._init(options);
      };
    ......
      return Sub
    };
  }

# 4. 内置关系

**prototype: ** 显式原型对象存在于类中

__proto__ : 隐式原型对象存在于对象中

每个对象都有一个原型对象,通过函数创建的对象也将拥有这个原型对象。原型是一个指向对象的指针。

  • 可以将原型理解为对象的父亲,对象从原型对象继承来属性
  • 所有函数的原型默认是 Object的实例,所以这是可以使用toString/toValues/isPrototypeOf 等方法的原因
  • 使用原型对象为多个对象共享属性或方法
  • 如果对象本身不存在属性或方法将到原型上查找
  • 使用原型可以解决,通过构造函数创建对象时复制多个函数造成的内存占用问题
  • 原型包含 constructor 属性,指向构造函数
  • 对象包含 __proto__ 指向他的原型对象

内置关系

# 2. 单文件组件

以.vue后缀结尾的文件就是一个组件

School.vue

<template>
  <div>
    <h2>学校名:{{ name }}</h2>
    <h2>学校地址:{{ address }}</h2>
    <button @click="flag = !flag">点击修改地址</button>
    <br>
    <input v-show="flag" @keyup.enter="hideText()" v-model="address" type="text">
    <hr>
    <student></student>
  </div>
</template>
<script>
import student from "./Student.vue"
export default {
  name: "school",
  components: {
    student,
  },
  data() {
    return {
      name: "希望小学",
      address: "中国",
      flag: false,
    };
  },
  methods: {
    hideText() {
      this.flag = !this.flag;
    },
  },
};
</script>

Student.vue

<template>
  <div>
    <h2>学生姓名:{{ name }}</h2>
    <h2>学生年龄:{{ age }}</h2>
  </div>
</template>

<script>
export default {
  name: "student",
  data() {
    //这里存放数据
    return {
      name: "小明",
      age: 13,
    };
  },
};
</script>

App.vue: 管理所有组件

<template>
  <div>
    <school></school>
  </div>
</template>

<script>
import school from "./School.vue";
export default {
  name: "app",
  components: {
    school,
  },
};
</script>

main.js

import app from './App.vue'

new Vue({
	el: '#root',
	component: {app},
})


# 3. 相关小知识

# 1. ref标签属性

  • 被用来给元素或子组件注册引用信息(id的替代者)
  • 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象
  • 使用方式: 打标识:

    .....

    或 获取:this.$refs.xxx

# 2. props配置项

作用:接受父组件传过来的数据

说明: props中的数据是只读的不能进行修改,如果需要修改可以用data中的属性来接收props中需要修改的属性

<!-- 传递数据 -->
<school name="aa" address="bb"></school>

<script>
    // 第一种:简单接收
    props: ['name','address']
   	// 第二种:限制类型
    props: {
    	name:String,
       	address: String    
  	}
    // 第三种:限制类型,必要性,默认值
    props: {
        name: {
          type: String,
          required: true,
        },
        address: {
          type: String,
          default: "xxx",
        }
 	 }
</script>

# 3. mixin配置项

作用:将多个组件共用配置提取成一个混入对象

// 1.定义混入(外部定义一个x.js)
default const mx = {
data() {
    return {
        xxx:xxx
    }
},
    ...
}

// 2.引入混入
// (1)局部引入
import {mx} from 'xxx/x.js '
// 使用
mixins:[mx]
// (2)全局引入
Vue.mixin(mx)

# 4. 插件

  1. 功能:用于增强Vue
  2. 本质:包含install方法的一个对象,install的第一个参数是Vue,后面的参数是插件使用者传递的数据
  3. 使用:Vue.use(pl,args...)
const pl = {
    install(Vue,args...) {
        ....
    }
}

# 5. scoped-样式

作用:让样式在局部生效,防止冲突。

<style scoped>
</style>

# 6. nextTick

作用:在下一次DOM更新结束后执行指定回调

说明:当数据改变后想要基于更新后的DOM进行操作时使用

语法:this.$nextTick(回调)

# 7. slot-插槽

  • 作用:接受父组件传过来的HTML结构

  • 分类:默认插槽、具名插槽、作用域插槽

  • 使用:

    1. 默认插槽

      <!-- 父组件 -->
      <template>
          <Search>
              <div>xxx</div>
          </Search>
      </template>
      
      <!-- 子组件 -->
      <template>
          <div>
              <slot>默认内容</slot>
          </div>
      </template>
      
    2. 具名插槽

      <!-- 父组件 -->
      <template>
          <Search>
              <div slot="center">xxx</div>
              <template slot="footer">
              	<div>xxx</div>
      		</template>
          </Search>
      </template>
      
      <!-- 子组件 -->
      <template>
          <div>
              <slot name="center">默认内容</slot>
              <slot name="footer">默认内容</slot>
          </div>
      </template>
      
    3. 作用域插槽

      <!-- 父组件 -->
      <template>
          <Search>
              <div slot="center" slot-scope="data">{{data.msg}}</div>
              <template slot="footer">
              	<div>xxx</div>
      		</template>
          </Search>
      </template>
      
      <!-- 子组件 -->
      <template>
          <div>
              <slot name="center" msg="向父组件传递的数据">默认内容</slot>
              <slot name="footer">默认内容</slot>
          </div>
      </template>
      

# 4. 自定义事件

<!-- 方式一 通过v-on绑定一个custom事件触发deleteName方法 -->
<School @custom="deleteName"></School>
<!-- 方式二 通过this.$refs.xxx.$on -->
<School ref="st"></School>
mounted() {
	this.$refs.st.$on('custom', this.deleteName)
}
<!-- 子组件中触发事件 -->
this.$emit('custom', studentName)

局限:

  1. 此方式只用于子组件向父组件发送消息(数据)
  2. 问题: 隔代组件或兄弟组件间通信此种方式不合适

# 5. 全局事件总线

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

使用:

mounted() {
  // 父组件在总线上绑定事件
  this.$bus.$on('xxxx',this.demo)
}
// 子组件触发事件
methods: {
   xx() {
     this.$bus.$emit('xxxx',param)  
   } 
},
beforeDestroy() {
    // 组件销毁时用$off解绑当前组件所用到的事件。
    this.$bus.$off("xxxx")
}

# 三、Vue脚手架使用

npm                                     yarn

npm init                                yarn init              // 初始化
npm i | install                         yarn  (install)        // 安装依赖包
npm i x --S | --save                    yarn add  x            // 安装生产依赖并保存包名
npm i x --D | --save-dev                yarn add x -D          // 安装开发依赖并保存包名
npm un | uninstall  x                   yarn remove            // 删除依赖包
npm i -g | npm -g i x                   yarn global add x      // 全局安装
npm un -g x                             yarn global remove x   // 全局下载
npm run dev                             yarn dev | run dev     // 运行命令

# 1. 安装

npm install -g @vue/cli
# OR
yarn global add @vue/cli

检查其版本是否正确:

vue --version

# 2. 创建一个项目

vue create hello-world

帮助命令

vue create --help
  -p, --preset &lt;presetName>       忽略提示符并使用已保存的或远程的预设选项
  -d, --default                   忽略提示符并使用默认预设选项
  -i, --inlinePreset &lt;json>       忽略提示符并使用内联的 JSON 字符串预设选项
  -m, --packageManager &lt;command>  在安装依赖时使用指定的 npm 客户端
  -r, --registry &lt;url>            在安装依赖时使用指定的 npm registry
  -g, --git [message]             强制 / 跳过 git 初始化,并可选的指定初始化提交信息
  -n, --no-git                    跳过 git 初始化
  -f, --force                     覆写目标目录可能存在的配置
  -c, --clone                     使用 git clone 获取远程预设选项
  -x, --proxy                     使用指定的代理创建项目
  -b, --bare                      创建项目时省略默认组件中的新手指导信息
  -h, --help                      输出使用帮助信息

使用图形化界面

你也可以通过 vue ui 命令以图形化界面创建和管理项目:

vue ui

上述命令会打开一个浏览器窗口,并以图形化界面将你引导至项目创建的流程。

# 3. 组件化编码流程

(1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。

(2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

1).一个组件在用:放在组件自身即可。

2). 一些组件在用:放在他们共同的父组件上(状态提升)。

(3).实现交互:从绑定事件开始。

props适用于: (1).父组件 ==> 子组件 通信

(2).子组件 ==> 父组件 通信(要求父先给子一个函数)

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

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

# 4. 配置代理

vue.config.js中配置代理规则:

// 这会告诉开发服务器将任何未知请求 (没有匹配到静态文件的请求) 代理到`http://localhost:4000`。
module.exports = {
  devServer: {
    proxy: 'http://localhost:4000'
  }
}
  • 优点:配置简单
  • 缺点:不能配置多个代理以及是否走代理
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:4000',
        // 是否启用websocket
        ws: true,
        changeOrigin: true,
        // 匹配正则的路径替换为''
        pathRewrite: {'^api': ''}
      },
      '/foo': {
        target: 'http://localhost:5000'
      }
    }
  }
}

# 四、Vuex

概念:在Vue中实现集中式状态(数据)管理的一个插件,对多个组件的共享数据进行集中式管理,是一种组件间通信的方式

状态自管理应用包含以下几个部分:

  • state,驱动应用的数据源;
  • view,以声明方式将 state 映射到视图;
  • actions,响应在 view 上的用户输入导致的状态变化。

“单向数据流”理念的简单示意:

单向数据流

多个组件共享状态时,单向数据流的简洁性很容易被破坏:

  • 多个组件依赖于同一数据

  • 不同组件的行为需要改变同一数据

    Vuex流程图

引入:

./store/index.js

/**
 * 创建Vuex中的核心store
 */
import Vue from 'vue';
import Vuex from 'vuex';
// 使用插件
Vue.use(Vuex);
// 响应组件中的动作
const actions = {};

// 加工数据(state)
const mutations = {};

// 存储数据
const state = {};

// 当state中的数据需要经过加工后再使用时,可以使用getters加工。
const getters = {};

export  default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
})

main.js

import Vue from 'vue'
import App from './App'
import store from './store/index'
Vue.config.productionTip = false

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

使用:

methods: {
  increment() {
    // 分发 Action
    this.store.dispatch('increment',args)
    // 直接处理 this.$store.commit('increment')
    console.log(this.$store.state.count)
  }
}

const store = new Vuex.Store({
  state: {
    count: 0
  },
  // mutation 必须是同步函数
  mutations: {
    increment (state,value) {
      state.count++
    }
  },
  actions: {
    // context为上下文相当于mini的store
    increment (context,value) {
      // 提交载荷(Payload)
      context.commit('increment')
    }
  }
})

# 1. 四个map方法的使用

  1. mapState方法:用于帮助我们映射state中的数据为计算属性

    computed: {
        //借助mapState生成计算属性:sum、school、subject(对象写法)
         ...mapState({sum:'sum',school:'school',subject:'subject'}),
             
        //借助mapState生成计算属性:sum、school、subject(数组写法)
        ...mapState(['sum','school','subject']),
    },
    
  2. mapGetters方法:用于帮助我们映射getters中的数据为计算属性

    computed: {
        //借助mapGetters生成计算属性:bigSum(对象写法)
        ...mapGetters({bigSum:'bigSum'}),
    
        //借助mapGetters生成计算属性:bigSum(数组写法)
        ...mapGetters(['bigSum'])
    },
    
  3. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

    methods:{
        //靠mapActions生成:incrementOdd、incrementWait(对象形式)
        ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
        //靠mapActions生成:incrementOdd、incrementWait(数组形式)
        ...mapActions(['jiaOdd','jiaWait'])
    }
    
  4. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

    methods:{
        //靠mapActions生成:increment、decrement(对象形式)
        ...mapMutations({increment:'JIA',decrement:'JIAN'}),
        
        //靠mapMutations生成:JIA、JIAN(对象形式)
        ...mapMutations(['JIA','JIAN']),
    }
    

备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

# 2. 模块化+命名空间

  1. 目的:让代码更好维护,让多种数据分类更加明确。

  2. 修改store.js

    const countAbout = {
      namespaced:true,//开启命名空间
      state:{x:1},
      mutations: { ... },
      actions: { ... },
      getters: {
        bigSum(state){
           return state.sum * 10
        }
      }
    }
    
    const personAbout = {
      namespaced:true,//开启命名空间
      state:{ ... },
      mutations: { ... },
      actions: { ... }
    }
    
    const store = new Vuex.Store({
      modules: {
        countAbout,
        personAbout
      }
    })
    
  3. 开启命名空间后,组件中读取state数据:

    //方式一:自己直接读取
    this.$store.state.personAbout.list
    //方式二:借助mapState读取:
    ...mapState('countAbout',['sum','school','subject']),
    
  4. 开启命名空间后,组件中读取getters数据:

    //方式一:自己直接读取
    this.$store.getters['personAbout/firstPersonName']
    //方式二:借助mapGetters读取:
    ...mapGetters('countAbout',['bigSum'])
    
  5. 开启命名空间后,组件中调用dispatch

    //方式一:自己直接dispatch
    this.$store.dispatch('personAbout/addPersonWang',person)
    //方式二:借助mapActions:
    ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
  6. 开启命名空间后,组件中调用commit

    //方式一:自己直接commit
    this.$store.commit('personAbout/ADD_PERSON',person)
    //方式二:借助mapMutations:
    ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
    

# 五、Vue Router

# 1. 基本使用

  1. 安装vue-router,命令:yarn add vue-router

  2. 应用插件:Vue.use(VueRouter)

  3. 编写router配置项:

    // 引入VueRouter
    import VueRouter from 'vue-router'
    // 引入组件
    import About from '../components/About'
    import Home from '../components/Home'
    
    // 创建router实例对象,去管理一组一组的路由规则
    const router = new VueRouter({
    	routes:[
    		{
    			path:'/about',
    			component:About
    		},
    		{
    			path:'/home',
    			component:Home
    		}
    	]
    })
    
    // 暴露router
    export default router
    
  4. 实现切换(active-class可配置高亮样式)

    <router-link active-class="active" to="/about">About</router-link>
    
  5. 指定展示位置

    <router-view></router-view>
    

# 2. 几个注意点

  1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  4. 整个应用只有一个router,可以通过组件的$router属性获取到。

# 3. 嵌套路由

  1. 配置路由规则,使用children配置项:

    routes:[
    	{
    		path:'/about',
    		component:About,
    	},
    	{
    		path:'/home',
    		component:Home,
    		children:[ //通过children配置子级路由
    			{
    				path:'news', //此处一定不要写:/news
    				component:News
    			},
    			{
    				path:'message',//此处一定不要写:/message
    				component:Message
    			}
    		]
    	}
    ]
    
  2. 跳转(要写完整路径):

    <router-link to="/home/news">News</router-link>
    

# 4. 路由的query参数

  1. 传递参数

    <!-- 跳转并携带query参数,to的字符串写法 -->
    <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
    				
    <!-- 跳转并携带query参数,to的对象写法 -->
    <router-link 
    	:to="{
    		path:'/home/message/detail',
    		query:{
    		   id:666,
                title:'你好'
    		}
    	}"
    >跳转</router-link>
    
  2. 接收参数:

    $route.query.id
    $route.query.title
    

# 5. 命名路由

  1. 作用:可以简化路由的跳转。

  2. 如何使用

    1. 给路由命名:

      {
      	path:'/demo',
      	component:Demo,
      	children:[
      		{
      			path:'test',
      			component:Test,
      			children:[
      				{
                          name:'hello' //给路由命名
      					path:'welcome',
      					component:Hello,
      				}
      			]
      		}
      	]
      }
      
    2. 简化跳转:

      <!-- 简化前,需要写完整的路径 -->
      <router-link to="/demo/test/welcome">跳转</router-link>
      
      <!-- 简化后,直接通过名字跳转 -->
      <router-link :to="{name:'hello'}">跳转</router-link>
      
      <!-- 简化写法配合传递参数 -->
      <router-link 
      	:to="{
      		name:'hello',
      		query:{
      		   id:666,
                  title:'你好'
      		}
      	}"
      >跳转</router-link>
      

# 6. 路由的params参数

  1. 配置路由,声明接收params参数

    {
    	path:'/home',
    	component:Home,
    	children:[
    		{
    			path:'news',
    			component:News
    		},
    		{
    			component:Message,
    			children:[
    				{
    					name:'xiangqing',
    					path:'detail/:id/:title', //使用占位符声明接收params参数
    					component:Detail
    				}
    			]
    		}
    	]
    }
    
  2. 传递参数

    <!-- 跳转并携带params参数,to的字符串写法 -->
    <router-link :to="/home/message/detail/666/你好">跳转</router-link>
    				
    <!-- 跳转并携带params参数,to的对象写法 -->
    <router-link 
    	:to="{
    		name:'xiangqing',
    		params:{
    		   id:666,
               title:'你好'
    		}
    	}"
    >跳转</router-link>
    

    特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

  3. 接收参数:

    $route.params.id
    $route.params.title
    

# 7.路由的props配置

​ 作用:让路由组件更方便的收到参数

{
	name:'xiangqing',
	path:'detail/:id',
	component:Detail,

	// 第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
	// props:{a:900}

	// 第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
	// props:true
	
	// 第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
	props(route){
		return {
			id:route.query.id,
			title:route.query.title
		}
	}
}

# 8. replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  3. 如何开启replace模式:<router-link replace .......>News</router-link>

# 9. 编程式路由导航

  1. 作用:不借助<router-link>实现路由跳转,让路由跳转更加灵活

  2. 具体编码:

    //$router的两个API
    this.$router.push({
    	name:'xiangqing',
    		params:{
    			id:xxx,
    			title:xxx
    		}
    })
    
    this.$router.replace({
    	name:'xiangqing',
    		params:{
    			id:xxx,
    			title:xxx
    		}
    })
    this.$router.forward() //前进
    this.$router.back() //后退
    this.$router.go() //可前进也可后退
    

# 10. 缓存路由组件

  1. 作用:让不展示的路由组件保持挂载,不被销毁。

  2. 具体编码:

    <keep-alive include="News"> 
        <router-view></router-view>
    </keep-alive>
    

# 11. 路由守卫

作用:对路由进行权限控制

分类:全局守卫、独享守卫、组件内守卫

完整的导航解析流程:

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter。
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter。
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

# 全局守卫

每个守卫方法接收三个参数:

  • to: Route: 即将要进入的目标 路由对象

  • from: Route: 当前导航正要离开的路由

  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数

    • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
    • next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
    • next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop (opens new window) 或 router.push (opens new window) 中的选项
    • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() (opens new window) 注册过的回调
// 1.全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})

// 2.全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
	console.log('afterEach',to,from)
	if(to.meta.title){ 
		document.title = to.meta.title //修改网页的title
	}else{
		document.title = 'vue_test'
	}
})

// 3.全局解析守卫 2.5.0 新增
// 在 2.5.0+ 你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

# 独享守卫

在路由配置上直接定义 beforeEnter 守卫:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      // 这些守卫与全局前置守卫的方法参数是一样的
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

# 组件内守卫

可以在路由组件内直接定义以下路由导航守卫:

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 新增)
  • beforeRouteLeave
const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。

不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。

beforeRouteUpdate (to, from, next) {
  // just use `this`
  this.name = to.params.name
  next()
}

这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。

beforeRouteLeave (to, from, next) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}
编辑 (opens new window)
上次更新: 2024/06/12, 02:20:36
vue3_base

vue3_base→

Theme by Vdoing | Copyright © 2024-2024 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式