跳至主要內容

vue

wushengzhu大约 27 分钟前端框架frontend

vue 是什么?

一套用于构建用户界面的渐进式 JavaScript 框架。Vue 可以自底向上逐层的应用。2015 年 10 月 27 日正式发布 Vue1.0 版本,2016 年 10 月 1 日发布 Vue2.0 版本,2020 年 9 月 18 日正式发布 Vue3.0 版本。

vue 特点?

  • 采用组件化模式,提高代码复用率,且让代码更好维护。

  • 声明式编码,让编码人员无需直接操作 DOM,提高开发效率

    声明式编码如 v-for、模板语法等,命令式编码:需要用到 document 操作 DOM

  • 使用虚拟 DOM+优秀的 Diff 算法,尽量复用 DOM 节点。

Vue2-学习笔记

1 vue 基础

1.1 Vue 实例化

  • Vue 工作,必须创建一个 Vue 实例,且要传入一个配置对象.
  • root 容器里的代码依然符合 html 规范,只不过混入了一些特殊的 Vue 语法。
  • root 容器里的代码被称为 Vue 模板。
  • Vue 实例和容器是一一对应的。
  • 模板中语法中的 xxx 要写 js 表达式,且 xxx 可以自动读取到 data 中所有属性。
  • 一旦 data 中数据发生改变,那么模板中用到该数据的地方也会自动更新。
new Vue({
  el: '#root', // el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串。
  data: {
    name: '',
  },
})
<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  </head>
  <body>
    <div id="app"></div>
  </body>
  <script>
    const app = new Vue({
      el: '#app',
      data: {},
    })
  </script>
</html>

注意区分:js 代码(语句:const a=1;)和 js 表达式(一个表达式会生成一个值,可以放在任何一个需要值的地方,如 a+b)

1.2 模板语法

  • 插值语法:用于解析标签体内容,写法如下,xxx 是 js 表达式,且可以直接读取到 data 中所有属性。

    <div>{{name}}</div>
    
  • 指令语法:用于解析标签(包括标签属性、标签体内容、绑定事件...),如 v-bind:href=“xxx”,xxx 同样写 js 表达式,且可以直接读取到 data 中的所有属性。

    <a v-bind:href="url"></a>
    

1.3 数据绑定

  • 单向绑定:只能从 data 中传递值,value 改变值不能改变存到 data 中的变量,通俗来说就是数据只能从 data 流向 data。

    单向数据绑定:
    <input v-bind:value="name1" />
    
  • 双向绑定:data 中声明的默认值改变,value 可以改变;value 的改变同时可以改变存到 data 中的值,通俗来说就是数据不仅能从 data 流向页面,还可以从页面流向 data。

    双向数据绑定:
    <input v-model="name2" />
    <br />
    

​ 注意:v-model 只能用在表单元素,如果用在 h2 这种非表单元素是不支持报错的。v-model:value 可以简化为 v-model。

1.4 el 与 data 的两种写法

// el第一种写法
const v = new Vue({
  el: '#root',
  data: {
    name: 'abc',
  },
})

// el第二种写法
const v = new Vue({
  data: {
    name: 'abc',
  },
})
v.$mount('#root')
// data的第一种写法,在html中写vue就用这种
const v = new Vue({
  el: '#root',
  data: {
    name: 'abc',
  },
})

// data的第二种写法,写组件的时候必须用这种
const v = new Vue({
  el: '#root',
  data() {
    return {
      name: 'abc',
    }
  },
})

注意地,data 不能写成箭头函数。

1.5 理解 MVVM

  • M:模型(Model),对应 data 中的数据。
  • V:视图(View),模板 template。
  • VM:视图模型(ViewModel),Vue 实例对象。

data 中所有的属性,最后都出现在 vm 身上了。vm 身上的所有属性及 Vue 原型上所有属性,在 Vue 模板中都可以直接使用。

1.6 数据代理

  • Object.defineProperty:它有三个属性:目标对象、需要添加的属性、当前属性配置(是否可枚举、是否可写、是否可删除)。

  • 数据代理:通过一个对象代理对另一个对象中属性的操作。

    let obj = {x:100};
    let obj2 = {y:200};
    Object.defineProperty(obj2,'x',{
        get(){
         return obj.x;
        }
        set(value){
    
        }
    })
    

    vm._data === data,vue 中的数据代理通过 vm 对象来代理 data 对象中属性的操作。vue 中的数据代理作用是为了更加方便的操作 data 中的数据,基本原理:通过 defineProperty 把 data 对象中所有属性添加的 vm 上,为每一个添加到 vm 上的属性,都指定一个 getter/setter,在 getter/setter 内部去操作 data 中对应的属性。

    let data = {
      name: 'abc',
    }
    const v = new Vue({
      el: '#root',
      data,
    })
    

1.7 事件处理

  • 事件的基本使用

    • 使用 v-on:xxx 或@xxx 绑定事件,其中 xxx 是事件名

    • 事件的回调需要配置在 methods 对象中,最终会在 vm 了

    • methods 中配置的函数,不需要箭头函数,否则 this 就不是 vm 了

    • methods 中配置参数的函数,都是被 Vue 所管理的函数,this 的指向是 vm 或组件实例对象;

  • 事件的修饰符(事件后加.事件修饰符,比如 click.prevent)

    • prevent:阻止默认事件
    • stop:阻止事件冒泡
    • once:事件只触发一次
    • capture:使用事件的捕获模式
    • self:只有 event.target 是当前操作的元素才触发事件
    • passive:事件默认行为立即执行,无须等待事件回调执行完毕。
  • 键盘事件:如@keyup.esc

    • 回车 => enter
    • 删除 => delete
    • 退出 => esc
    • 空格 => space
    • 换行 => tab
    • 上(up)下(down)左(left)右(right)

1.8 计算属性与监听

1.8.1 计算属性-computed

定义:要用的属性不存在(定义在 computed 里的属性没有在 data 中定义的),要通过已有属性(在 data 中已经定义的)计算得来。

原理:底层借助了 Object.defineproperty 方法提供的 getter 和 setter。get 函数什么时候被调用:1、初次读取时会执行一次。当依赖的数据发生变化改变时会被再次调用。具有缓存机制。

data:{
  firstName:'',
  lastName:''
},
computed:{
  fullName(){
    return this.firstName+this.lastName;
  }
}

1.8.2 计算属性-watch

1.8.3 watch 与 computed 对比

  • computed 能完成的功能,watch 都可以完成
  • watch 能完成的功能,computed 不一定能完成,如:watch 可以进行异步操作
  • 注意:被 Vue 管理的函数,最好写成普通函数,这样 this 的指向才是 vm 或组件实例对象。
  • 注意:所有不被 Vue 管理的函数(定时器的回调函数、ajax 的回调函数、promise 的回调函数等),最好写成箭头函数,这样 this 的指向才是 vm 或组件实例对象。

1.9 class 绑定与 style 绑定

1.9.1 class 绑定

  • 绑定 class 方式一:字符串写法,适用于:样式的类名不确定,需要动态指定。
  • 绑定 class 方式二:数组写法,适用于:要绑定的样式个数不确定、名字也不确定。
  • 绑定 class 方式三:对象写法,适用于:样式个数确定、名字也确定、但要动态决定要不要用。
<div :class="dynamicClass"></div>
data:{
  dynamicClass:'user',
}

1.9.2 style 绑定

  • 绑定 style 样式方式之一:对象写法
  • 绑定 style 样式方式之二:数组写法
<div :style="color:curcolor"></div>
data:{
  curcolor:'#000'
}

1.10 条件渲染与列表渲染

1.10.1 条件渲染

  • v-show:不可以与 template 配合使用。适用于切换频率较高的场景。DOM 元素未被移除,只是使用样式隐藏掉。
  • v-if 或 v-else:可以与 template 配合使用。适用于切换频率较低的场景。不展示的 DOM 元素会直接被移除。

1.10.2 列表渲染

1.11 key 作用与原理

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

  • 对比规则:

    1)旧虚拟 DOM 中找到与新虚拟 DOM 相同的 key:若虚拟 DOM 中内容没变,直接使用之前的真实 DOM;若虚拟 DOM 中内容变了,则生成新的真实 DOM,随后渲染到页面。

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

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

1.12 列表过滤与排序

1.13 Vue 监测数据的原理

  • Vue.set()方法

可以动态增加没有在 data 中定义的属性

 <div id="root">
      名字:{{student.name}}<br />
      性别:{{student.sex}}<br />
      年龄:{{student.age}}<br />
    </div>
    <script type="text/javascript">
      Vue.config.productionTip = false;
      const vm = new Vue({
        el: "#root",
        data: {
          student: {
            name: "孙悟空",
            age: 500,
          },
            hobby:[]
        },
      });
// 修改data里的对象
// vm.$set(vm._data.student,'sex','男')
// Vue.set(vm._data.student,'sex','男')

// 修改data里的数组
vm.$set(vm._data.hobby, 1, '游泳')
// 也可以合理引用Vue官网认可修改数组元素的方法,如push,可查看官方文档

注意地,这两种方法只能给 data 对象里的对象添加属性,可以添加数组数据,不能直接在 data 根数据添加属性。主要是 data 里定义的非数组属性都会包含有 get、set 方法。

  • Vue 检测对象原理:通过 setter 实现,且在 new Vue 实例化时就传入要监测的数组。对象后加的属性,Vue 默认不做响应处理,如果需要做响应处理,使用 set 方法。

  • Vue 检测数组的原理:通过包裹数组更新元素的方法实现,本质就是做了两件事:调用原生对应的方法对数组进行更新;重新解析模板,进而更新页面。在 Vue 中修改数组使用 set 方法或 push、pop、shift、unshift、splice、sort、reverse。

1.14 收集表单数据

<div id="root">
  <form @submit.prevent="demo">
    账号:<input type="text" v-model="userInfo.username" /><br /><br />
    密码:<input type="password" v-model="userInfo.password" /><br /><br />
    性别:
    <input type="radio" name="sex" v-model="userInfo.sex" value="male" />
    <input type="radio" name="sex" v-model="userInfo.sex" value="female" />
    <br /><br />
    爱好:<input type="checkbox" v-model="userInfo.hobby" value="学习" /> 学习
    <input type="checkbox" v-model="userInfo.hobby" value="游戏" />游戏
    <input type="checkbox" v-model="userInfo.hobby" value="编程" />编程
    <br /><br />
    <button>提交</button>
  </form>
</div>
<script type="text/javascript">
  Vue.config.productionTip = false
  const vm = new Vue({
    el: '#root',
    data: {
      userInfo: {
        username: '',
        password: '',
        sex: 'male',
        hobby: [],
      },
    },
    methods: {
      demo() {
        console.log(this.userInfo)
      },
    },
  })
</script>

1.15 过滤器

  • 过滤器:对数据进行格式化
// 计算属性实现
// methods实现
// 过滤器实现:可以使用多个过滤器
<div>价格:{{money|moneyFormat}}</div>

// 局部过滤器
filters: {
	moneyFormat(value) {
	return "¥" + value;
	},
},

// 全局过滤器
Vue.filter("moneyFormat", (value) => {
    return "¥" + value;
});

1.16 指令

1.16.1 Vue 内置指令

  • v-bind:单向绑定解析表达式,可简写为:xxx。

  • v-model:双向数据绑定。

  • v-for:遍历数组/对象/字符串。

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

  • v-if:动态控制节点是否存在。

  • v-show:动态控制节点是否展示。

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

    <div>你好,{{name}}</div>
    <!-- name内容覆盖掉内容:你好 -->
    <div v-text="name">你好</div>
    
  • v-html

  • v-cloak:没有值,是一个特殊属性,Vue 实例创建完毕并接管容器后,会删掉 v-cloak 属性;使用 css 配合 v-cloak 可以解决网速慢时页面展示。

    // css:当数据还没有加载出来就不显示
    [v-cloak]{
      display:none;
    }
    
    <h1 v-cloak>{{name}}</div>
    
  • v-once:只渲染一次,在节点初次到动态渲染后,就视为静态内容了。以后数据的改变不会引起 v-once 所在结构的更新,可以用于优化性能。

    <h1 v-once>初始化n值:{{n}}</h1>
    <h1>当前n值:{{n}}</h1>
    <button @click="n++">增加n</button>
    
  • v-pre:跳过其所在节点的编译过程,可利用它跳过:没有使用指令语法,没有使用插值语法的节点,会加快节点。

    <h1 v-pre>前端,你好1</h1>
    <h1>当前n值:{{n}}</h1>
    <button @click="n++">增加n</button>
    

1.16.2 自定义指令

  • 函数式:什么时候调用指令函数,指令与元素成功绑定时;指令所在的模板被重新解析时。

    <h2>放大10倍n值:<span v-big="99+1+2"></span></h2>
    
    // element:真实Html
    // binding:绑定真实
    big(element, binding) {
    // console.log(element, binding);
    element.innerText = binding.value * 10;
    },
    
  • 对象式:

    <h1>当前n值:{{n}}</h1>
    <button @click="n++">增加n</button><br />
    <input type="text" v-fbind:value="n" />
    
    directives: {
        fbind: {
        // 指令与元素成功绑定时
        bind(element, binding) {
        element.value = binding.value;
        },
        // 指令所在元素被插入页面
        inserted(element, binding) {
        // 初始化时焦点聚焦
        element.focus();
        },
        // 指令所在的模板被重新解析
        update(element, binding) {
        element.value = binding.value;
        // 数据更新时也聚焦
        element.focus();
        },
    },
    

1.17 Vue 的生命周期

<div id="root">{{info}}</div>
<script type="text/javascript">
  Vue.config.productionTip = false
  const vm = new Vue({
    el: '#root',
    data: {
      info: '北极光之夜',
    },
    methods: {
      show(info, obj) {
        console.log(info)
        console.log('获取Vue实例data里的数据:', obj.info)
        console.log('挂载的对象,就是DOM:', obj.$el)
        console.log(
          '页面上已经挂载的DOM:',
          document.getElementById('root').innerHTML
        )
      },
    },
  })
</script>
  • beforeCreate:此时 data 对象与 methods 这些都没有初始化好。data 里数据和 methods 都调不了。

    beforeCreate: function () {
       this.show("vue实例初始化之后", this);
    },
    
  • created:data 已经创建,可以读取 data 中数据,但是 DOM 还没渲染,也就是页面还没挂载。

    created: function () {
       this.show("vue实例初始化之后", this);
    },
    // vue实例初始化之后
    // 获取Vue实例data里的数据: 北极光之夜
    // 挂载的对象,就是DOM: undefined
    // 页面上已经挂载的DOM: {{info}}
    
  • beforeMount:完成了模板的编译,还没挂载,DOM 已经渲染,但是数据还没有渲染在页面上

    beforeMount: function () {
    this.show("挂载之前", this);
    },
    // 挂载之前
    // 获取Vue实例data里的数据: 北极光之夜
    // 挂载的对象,就是DOM
    // 页面上已经挂载的DOM: {{info}}
    
  • mounted:模板编译好了,也挂在到页面中,页面正常显示了。

    mounted: function () {
     this.show("挂载之后", this);
    },
    // 挂载之后
    // 获取Vue实例data里的数据: 北极光之夜
    // 挂载的对象,就是DOM: <div id="root">北极光之夜</div>
    // 页面上已经挂载的DOM: 北极光之夜
    
  • beforeUpdate:先在控制台执行 vm.info='王者荣耀'。data 已经更新,但是页面显示数据还是原来的,还没有重新开始渲染 DOM 树。

    beforeUpdate: function () {
    this.show("更新之前", this);
    },
    // 更新之前
    // 获取Vue实例data里的数据: 王者荣耀
    // 挂载的对象,就是DOM: <div id="root">王者荣耀</div>
    // 页面上已经挂载的DOM: 北极光之夜
    
  • updated:data 已经更新,但是页面显示数据还是原来的,重新开始渲染 DOM 树。

    beforeUpdate: function () {
    this.show("更新之后", this);
    },
    // 更新之后
    // 获取Vue实例data里的数据: 王者荣耀
    // 挂载的对象,就是DOM: <div id="root">王者荣耀</div>
    // 页面上已经挂载的DOM: 王者荣耀
    
  • beforeDestroy:vue 实例被销毁之前,vue 实例还能被使用。

    beforeDestroy: function () {
    this.show("销毁之前", this);
    },
    // 销毁之前
    // 获取Vue实例data里的数据: 北极光之夜
    // 挂载的对象,就是DOM: <div id="root">北极光之夜</div>
    // 页面上已经挂载的DOM: 北极光之夜
    
  • destroyed:这个阶段在 vue 实例销毁后调用,此时所有实例指示的所有东西都会解除绑定,事件监听器也都移除,子实例也被销毁。

    destroyed: function () {
    this.show("销毁之后", this);
    }
    // 销毁之后
    // 获取Vue实例data里的数据: 北极光之夜
    // 挂载的对象,就是DOM: <div id="root">北极光之夜</div>
    // 页面上已经挂载的DOM: 北极光之夜
    // undefined
    

2 vue 组件化编程

2.1 什么是组件?

  • 组件:实现应用中局部功能代码和资源的集合。

2.2 非单文件组件与单文件组件

  • 非单文件组件:一个文件中包含有 n 个组件。
  • 单文件组件:一个文件中只包含有 1 个组件。

2.3 组件的几个注意点

  • 组件命名:一个单词时(首字母小写:home;首字母大写:Home),多个单词时(驼峰命名需要脚手架支持:MySchool,横线分割:my-school)、命名尽量回避 html 元素名称,如 h1、h2。

2.4 组件嵌套

2.5 VueComponent

  • VueComponent:组件本质是一个名为 VueComponent 的构造函数,且不是程序员定义的,是Vue.extend生成的。我们只需要在 template 写组件,Vue 解析时会帮我们创建组件的实例对象,即 Vue 帮我们执行的new VueComponent(options)。注意地,每次调用 Vue.extend,返回的是一个全新的 VueComponent

  • 关于 this 的指向:

    组件配置:data 函数、methods 中的函数、watch 中的函数、cpmputed 中的函数,它们的 this 均是VueComponent 实例对象

    new Vue 配置:data 函数、methods 中的函数、watch 中的函数、cpmputed 中的函数,它们的 this 均是Vue 实例对象

3 vue-cli

3.1 创建脚手架

3.2 脚手架结构

3.3 render 函数(模板解析器)

vue.js 文件与 vue.runtime.xxx.js 的区别:

(1)vue.js 是完整版的 Vue,包含:核心功能+模板解析器

(2)vue.runtime.xxx.js 是运行版的 vue,只包含核心功能,没有模板解析器,所以不能配置 template 配置项,需要使用 render 函数接收到的 createElement 函数去指定具体内容。

3.4 修改脚手架默认配置

  • 查看 Vue 脚手架默认配置:

    vue inspect > output.js
    
  • 使用 vue.config.js 可以对脚手架进行个性化定制。

4 Vue 一些特性

4.1 ref 属性

  • 给标签添加 ref 属性:获取的是真实 DOM 元素。

  • 给组件添加 ref 属性:组件的实例对象。

4.2 props 配置

  • 传递数据:

    <Demo name="xxx"/>
    
  • 接收数据:第一种方式(只接收):props:['name'],第二种方式:限制类型,props:{name:Number},第三种方式:限制类型、限制必要性、指定默认值,props:{name:{type:String,required:true,default:'老王'}}

4.3 mixin 混入

  • 局部混合
// js文件封装方法
export const hunhe = {
  methods: {
    showName() {
      alert(this.name)
    },
  },
}
// vue组件
import {hunhe} '../mixins'
export default{
  data:{
    name:'书悟空',
  },
  mixins:[hunhe]
}
  • 全局混合

    Vue.mixin(xxx);
    

4.4 插件

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

  • 定义插件:

    对象.install = function (Vue,options){
      // 添加全局过滤器
      Vue.filter(....);
      // 添加全局指令
      Vue.directive(....);
      // 配置全局混入
      Vue.mixin(....);
      ....
    }
    
  • 使用插件:Vue.use()

4.5 scoped 样式

5 组件的编码流程

  • 拆分静态组件:组件要按照功能点拆分,命名不要与 html 元素冲突。
  • 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用。

6 浏览器的本地存储

  • localStorage:localStorage 存储的内容需要手动清除才会消失。
  • sessionStorage:存储的内容会随着浏览器关闭而消失。

7 组件的自定义事件

  • 自定义实现方法一:v-on 或@使用

    <Demo @add="test" />
    <Demo v-on:add="test" />
    
  • 自定义实现方法二:使用 ref

    this.$refs.xxxx.$on('test', this.test)
    
  • 触发自定义事件:

    this.$emit('add',数据)
    

​ 解绑所有自定义事件:

this.$off('事件名称') //解绑一个自定义事件
this.$off([]) // 解绑多个自定义事件

​ 销毁当前组件实例:

this.$destory()

8 全局事件总线(GlobalEventBus)

  • 全局事件总线:任意组件间通信。

    new Vue({
      el: '#app',
      render: (h) => h(App),
      beforeCreate() {
        Vue.prototype.$bus = this // 安装全局事件总线,$bus就是当前应用的vm
      },
    })
    

9 消息订阅与发布-pubsub

10 Vue 封装的过度与动画

作用:在插入、更新或移除 DOM 元素时,在合适的时候给元素添加样式类名,也就是添加 Vue 封装好的动画默认调用的 class 样式。

v-enter:进入的起点
v-enter-active:进入的过程中
v-enter-to:进入的终点

v-leave:进入的起点
v-leave-active:进入的过程中
v-leave-to:进入的终点
  • transition动画:Vue 的内置 Api,注意如果 transition 添加了 name 属性,样式的默认命名前缀 v-要改为 name 值-。不然样式不生效,。
<button @click="isShow = !isShow">显示/隐藏</button>
<transition appear>
  <h1 v-show="isShow">你好啊!</h1>
</transition>
h1 {
  background-color: orange;
}

.v-enter-active {
  animation: myani 0.5s linear;
}

.v-leave-active {
  animation: myani 0.5s linear reverse;
}

@keyframes myani {
  from {
    transform: translateX(-100%);
  }

  to {
    transform: translateX(0);
  }
}
  • 过度效果:不用动画只用过度实现上面内容。
<button @click="isShow = !isShow">显示/隐藏</button>
<transition name="hello" appear>
  <h1 v-show="isShow">你好啊!</h1>
</transition>
h1 {
  background-color: orange;
  /* 谁要动画就放在谁 */
  transition: 0.5s linear;
}
/* 元素进入的起点  元素离开的终点 */
.hello-enter,
.hello-leave-to {
  transform: translateX(-100%);
}
/* 元素进入的终点  元素离开的起点 */
.hello-leave,
.hello-enter-to {
  transform: translateX(0);
}
  • transition-group:可以同时实现多个元素的动画,每一个元素都需要设置 key 值。
  • 第三方的集成动画库Animate.css

11 Vue 配置代理

devServer.proxy:有 target、changeOrigin(改变 url 的 host,true 时,url 为 target)、ws、pathRewrite。

'/api':{
   target:'',
   changeOrigin:false,
   pathRewrite{
    '^/api':'';// 匹配到/api路径,由''代替
   }
}

12 Vuex

注意下,vue2 需要安装 vuex3 版本,vue3 需要安装 vuex4 版本

12.1 什么时候使用 vuex

  • 多个组件依赖同一状态 - 共享。
  • 来自不同组件的行为需要变更同一状态 - 共享。
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const state = {
  shareBike: '共享单车,暂时没有人使用!',
  isUsed: false,
}
const mutations = {
  USE_BIKE(state, value) {
    state.shareBike = value
  },
  IS_USED(state, value) {
    state.isUsed = value
  },
}
const actions = {
  useBike(context, value) {
    context.commit('USE_BIKE', value)
  },
  isUsed(context, value) {
    context.commit('IS_USED', value)
  },
}

const modules = {}
const getters = {}

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

13 vue-router

为什么需要路由,因为以前都是多页面应用,也就是说一个页面有多个页面跳转,也就是多个 html,体验不好。如果用路由实现单页面,就可以通过路由切换单页面,一个路径一个页面。

13.1 路由的几个注意点

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

13.2 嵌套(多级)路由

13.3 命名路由

也就是路由数组中定义了一个 name,在传参的时候可以简化路由字符串

<router-link :to="/home/message/detail"></router-link>

<router-link :to="{ name: 'detail' }"></router-link>

13.4 路由参数

  • query:

    字符串写法
    <router-link :to="/home/message/detail?id=666&&title=你好"></router-link>
    
    对象写法
    <router-link :to="{ path: '', query: {} }"></router-link>
    
  • params:

    path:'detail/:id/:title'
    
    <router-link :to="/home/message/detail/666/你好"></router-link>
    
    <router-link :to="{name:'detail',params:{id:,title:}}"></router-link>
    

13.5 路由的 props

让组件更方便获取路由参数。

  • props 的第一种写法:值为对象,该对象中所有 key-value 都会以 props 的形式传给组件
{
  name:,
  path:,
  component,
  props:{a:1,b:'666'}
}
  • props 的第二种写法:值为布尔值,若布尔值为真,就会把该路由组件收到的所有 params 参数,以 props 的形式传给组件。

    // 在组件中接收
    props: ['id', 'title']
    
  • props 的第三种写法:值为函数,返回值是一个对象.

    props(route){
      return {id:route.query.id,title:route.query.title}
    }
    
  • router-link:默认是使用栈存取路由(push),如果配置了 replace 就不是,直接替换掉上一条路由。

    <router-link replace :to="{name:'detail',params:{id:,title:}}"></router-link>
    

13.7 编程式路由导航

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

this.$router.push({
   name:'xiangqing',
   params:{
     id:xxx,
     title:xxxx
   }
})

this.$router.replace({
   ...
})

13.8 缓存路由

  • keep-alive:有一个属性 include(字符串或数组)指定需要缓存的组件。每次切换不会执行组件销毁

14 路由守卫

14.1 全局前置守卫(beforeEach)

  • beforeEach:初始化的时候被调用、每次路由切换之前被调用
// to:将要进入的路由
// from:将要离开的路由
// next:执行下去,放行,必须有。
router.beforeEach(to,form,next){
  ...
  next()
}

14.2 全局后置守卫(afterEach)

  • afterEach:初始化的时候被调用、每次路由切换之后被调用

14.3 独享路由守卫(beforeEnter)

  • beforeEnter:指定某个路由独立使用的守卫

14.4 组件内路由守卫

  • beforeRouteEnter:通过路由规则,进入该组件时被调用。
  • beforeRouteLeave:通过路由规则,离开该组件时被调用。

14.5 history 模式与 hash 模式

  • hash 模式:默认方式,兼容性好,带/#/后面的路径,只是前端工作需要添加的,服务器会忽略。
  • history 模式:路径都是提交给服务器的实在的资源路径,不会忽略,如果不做任何处理刷新页面会出现 404 错误。要想处理整个问题,这时候需要后端处理,比如安装中间件(安装 connect-history-api-fallback)。

Vue3-学习笔记

1 Vue3 带来什么?

  • 性能提升:打包大小减少 41%、初次渲染快 55%,更新渲染快 133%、内存减少 54%。
  • 源码升级:使用 proxy 代替 defineProperty 实现响应式、重写虚拟 DOM 的实现和 Tree-Shaking
  • 拥抱 Ts:可以更好支持 ts
  • 新的特性:组合 API(Composition API)、新的内置组件等

2 初识 setup

  • setup:Vue3 中一个新的配置项,值为一个函数,是所有 Composition API 的表演舞台,也就是组件中所用到的数据、方法等等,均要配置在 setup 中

  • setup 函数的两种返回值:

    1)返回一个对象,则对象中的属性、方法、在模板中均可以直接使用。

    2)返回一个渲染函数:则可以自定义渲染内容。

  • setup 两个注意点:

    1)尽量不要与 Vue2.x 配置使用,因为 Vue2 配置中可以访问到 setup 中的属性、方法。但在 setup 中不能访问到 Vue2 配置,如果有重名属性,setup 优先。

    2)setup 不能是一个 async 函数,因为返回值不再是 return 的对象,而是 promise,模板看不到 return 对象中属性。

3 ref 函数

setup(props,context) {
    const name = ref('张三');
    console.log(name);// RefImpl对象
    const obj = ref({
        name:'张三',
        age:15
    })
    const changeInfo = ()=>{
        name.value = '李四';
        obj.value.name = '李四';
        obj.value.age = '19'
    }
    return {
        name,
        obj,
        changeInfo,
    }
},

RefImpl:reference implement,引用实现。

ref 作用:定义一个响应式数据。接收的数据可以是基本类型,也可以是数据类型。

基本类型的数据:响应式依然是靠 Object.defineProperty()的 get 与 set 完成的。

对象类型的数据:内部“求助”了 Vue3 中的一个新函数:reactive 函数

4 reactive 函数

  • 作用:定义一个对象类型的响应式数据(基本数据类型不要用它,要用 ref 数据)。
  • 原理:内部基于 proxy 实现,通过代理对象操作源对象内部数据进行操作。
setup(props,context) {
    const name = ref('张三');
    const obj = reactive({
        name:'张三',
        age:15
    })
    const arr = reactive(['学习','游泳'])
    const changeInfo = ()=>{
        name.value = '李四';
        obj.name = '李四';
        obj.age = '19';
        arr[0] = '冲浪';
    }
    return {
        name,
        obj,
        changeInfo,
    }
},

5 响应原理

5.1 Vue2 的响应原理

  • 对象类型:通过 object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。
  • 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)

存在问题:

  • 新增属性、删除属性,界面不会更新。
  • 直接通过下标修改数组,界面不会自动更新。

5.2 Vue3 的响应原理

  • 实现原理:通过 proxy 代理,拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等;通过 Reflect 反射,对被代理对象的属性进行操作。

6 computed 计算属性

7 watch 侦听器

  • 监视 ref 所定义的一个响应式数据

    watch(a,(newVal,oldVal))
    
  • 监视 ref 所定义的多个响应式数据

    watch([],(newVal,oldVal))
    
  • 监听 reactive 所定义的一个响应式数据的全部属性,无法获取正确的 oldVal,deep 配置无效。

  • 监听 reactive 所定义的一个响应式数据中的某个属性

    watch([()=>person.name],(newVal,oldVal))
    
  • 监视 reactvie 所定义的一个响应式数据中的某些属性

    watch([()=>person.name,()=>person.age],(newVal,oldVal))
    
  • 监听 reactive 所定义的对象中某个嵌套对象属性,所以 deep 配置有效。

8 watchEffect 函数

  • watch:既要指明监视的属性,也要指明监视的回调。

  • watchEffect:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。

    watchEffect(()=>{ // 在回调函数中使用到什么属性就监听什么 })
    
  • watchEffect 优点像 computed:但 computed 注重计算出来的值(回调函数的返回值),所以必须要写返回值;watchEffect 更注重的是过程(回调函数的函数体),所以不用写返回值。

9 自定义 hook 函数

什么是 hook?

本质是一个函数,把 setup 函数中使用的 Composition API 进行了封装。类似于 vue2.x 中的 mixin。自定义 hook 的优势;复用代码,让 setup 中的逻辑更清楚易懂。

10 toRef 与 toRefs

toRef:创建一个 ref 对象,其 value 值指向另一个对象中的某个属性;要将响应式对象中的某个属性单独提供给外部使用时。

const name = toRef(person,'name');

toRefs 与 toRef 功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person);

11 shallowreactive 与 shallowRef

  • shallowreactive:只处理对象最外层属性的响应式(浅响应式)
  • shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理。

什么时候使用?

  • 如果有一个对象数据,结构比较深,但变化时只是外层属性变化===>shallowReactive
  • 如果有一个对象数据,结构比较深,后续功能不会修改对象中的属性,而是生新的对象来替换==>shallowRef

12 readonly 和 shallowReadonly

  • readonly:让一个响应式数据变为只读的(深只读)。
  • shallowreadonly:让一个响应式数据变为只读(浅只读)。
  • 应用场景:不希望数据被修改时。

13 toRaw 和 markRaw

  • toRaw:将一个由 reactive 生成的响应式对象为普通对象。

    使用场景:用于读取响应式对象的普通对象,对这个普通对象的所有操作,不会引起页面更新。

  • markRaw:标记对象,使其永远不会再成为响应式对象。

    使用场景:有些值不应设置为响应式的,例如复杂的第三类库等;当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。

let car = {name:'汽车'}
person.car = markRaw(car);// 新增car属性,如果不加markRaw就会成为person里的响应属性

14 customRef

  • customRef:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。

15 provide 与 inject

作用:实现祖孙组件间(跨级组件)的通信。

套路:父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据。一般跨级用这两个 api,父子直接用 props 就行。

16 响应式数据判断:isRef、isReactive、isReadonly、isProxy

  • isProxy:检查一个对象是否由 reactive 或 readonly 方法创建的代理。

17 Composition API 的优势

  • Options API 存在的问题:新增或者修改一个需求,就需要分别在 data,methods,computed 里修改。
  • Composition API 的优势:更加优雅的组织我们的代码,函数,让相关功能代码更加有序的组织在一起。

18 Fragment 组件

  • 在 Vue2 中:组件必须有一个根标签
  • 在 Vue3 中:组件可以没有根标签,内部默认会将多个标签包含在一个 Fragment 虚拟元素中
  • 好处:减少标签层级,减少内存占用。

19 Teleport 组件

  • Teleport:是一种能够将我们的组件 html 结构移动到指定位置的技术。

20 Suspense 组件

  • Suspense:等待异步组件时渲染一些二外内容,让内容有更好的用户体验。
<Suspense>
<template v-slot:default>
<test3/>
</template>
<template v-slot:fallback>
<h3>加载中...</h3>
</template>
</Suspense>