Vue基础记录

Vue

整体要学的
vue基础
vue-cli
vue-router
vuex
element-ui
vue3

简介

特点:

  1. 组件化
  2. 声明式编码
  3. 使用虚拟dom + 优秀Diff算法


在生成真实dom的时候,会先有一部虚拟dom,且新的虚拟dom会和旧的虚拟dom进行diff算法比较
如果有些dom没改,那么就可以不用再重新渲染了,提高性能

环境

安装node

node -> 16.20.2

切换淘宝镜像

npm install -g cnpm -registry=http://registry.npm.taobao.org

npm config set registry http://registry.npm.taobao.org/

使用了第二个,下一步才有用

安装vue

npm install -g @vue/cli

vscode中不给运行vue解决办法

set-ExecutionPolicy RemoteSigne 

问题就是脚本的执行策略不行,应该是安全问题

创建vue项目

create vue jplan

vue还不给输入大写

选自己的配置

Vuex + router + CSS Pre-processors

版本

2.x

路径选择

history router no

css预处理语言 less

配置文件 -> package.json

启动

npm run serve

引入element-ui

引入

npm i element-ui -S

目录下必须有package.json

在 main.js 中写入以下内容:

import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';

Vue.use(ElementUI);

new Vue({
  el: '#app',
  render: h => h(App)
});

引入Axios

安装

npm install axios
npm install vue-axios

main.js引入

//引入axios
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios,axios)

开发者工具devtools

vue2 只能用vue-devtools

Vue核心

模版语法

插值语法
{{ xxx}}

指令语法
v-bind 简写是 :xxx

数据绑定

单向的是 :value=" "
双向的是 v-model:value=" " 简写是 v-model=" "

v-model绑定的值不能是props传来的值,因为props传来的值是不可修改的
特别是对象的值,修改了props,别人还不知道,这不完蛋了吗

data的两种写法

对象式

    <script type="text/javascript">
        Vue.config.productionTip = false; //阻止vue启动时生成生产提示
        new Vue({
            el: "#app",
            data: {
                msg: 'wo de fuck'
            }
        })
    </script>

函数式

    <script type="text/javascript">
        Vue.config.productionTip = false; //阻止vue启动时生成生产提示
        new Vue({
            el: "#app",
            data() {
                return {
                    msg: 'hi'
                }
            }
        })
    </script>

像一个函数返回一个对象

一般使用函数式,组件开发的时候必须写函数式,所以以后写函数式就ok

MVVM

Vue没有完全遵循MVVM,受启发

M 是 Model 模型,也就是我们的数据
V 是 View 视图,模版的代码 ->我们自己写的代码
VM 是视图模型,也就是Vue

Vue类似于数据和视图之间的中间商,我们在js代码中写的data会出现在vm里边,我们在{{}}模版语法里边可以获取到这些数据

在vm里边会有监听器,监听dom

Object.defineProperty()

Object.defineProperty(obj, prop, descriptor)

obj
要定义属性的对象。

prop
一个字符串或 Symbol,指定了要定义或修改的属性键。

descriptor
要定义或修改的属性的描述符。

描述符
1.configurable:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。

2.enumerable:能不能枚举,这里就是能不能for遍历,默认值为false

3.writable:表示能否修改属性的值。默认值为false。

4.value:包含这个属性的数据值。默认值为undefined。
举例

let p = {}
object.defineProperty(p,'age',{
    ...
})

configurable 不能delete p.age
writable 不能 p.age = 12,这样是无效的
value是设置值

get,set
读取函数时调用get

写入属性时调用set

例如,假如我们想让对象p的age值变成响应式的,就可以使用get

    <script>
        let num = 1;
        let p = {
            name: 'jjking'
        }
        Object.defineProperty(p,"age",{
            enumerable: true,
            get() {
                console.log('获取值,此处是相当于是响应式的');
                
                return num;
            }
        })
        console.log(p);
    </script>

需要注意的是,get函数不能和属性value共存,会报错
这里如果我们之后修改num,重新获取age的值的话,就会更新

如何定义多个属性

var student = {};
Object.defineProperties(student,{
    name:{
        writable:false,
        value:"lisi"
    },
    age : {
        writable:true,
        value : 16,
    },
    sex:{
        get(){
            return '男';
        },
        set(v){
            p1.sex = v
        }
    }
})

数据代理

最简单的数据代理
通过一个对象代理对另一个对象属性的操作(读/写)

    <script>
        let obj1 = {x:100}
        let obj2 = {y:200}

        Object.defineProperty(obj2,'x',{
            get() {
                return obj1.x;
            },
            set(value) {
                obj1.x = value;
            }
        })
    </script>

此时我们就可以使用obj2来操作obj1了,并且obj1自己的改变,也会改变obj2的x值

  1. vue先是把data里边的数据放到vm里边,但是名字叫_data
  2. 然后通过Object.defineProperty()代理所有属性的操作,也就是这里的vm.name,和vm.address,这两个是代理对象

事件处理

基本使用
@xxx: 函数名

阻止默认时间prevent

    <div id="app">
        <a href="www.baidu.com" @click="showInfo">点我</a>

    </div>

    <script type="text/javascript">
        const vm = new Vue({
            el: '#app',
            data: {

            },
            methods: {
                showInfo(e) {
                    e.preventDefault();
                    alert("同学你好!");
                }
            }
        })
    </script>

a标签的默认事件是跳转
我们可以直接写e.preventDefault();

也可以用vue的@click.prevent

@click.prevent="showInfo"

事件冒泡
原生的写法

    <div id="app">
        <div @click="showDiv">
            <button @click="showInfo">点我</button>

        </div>

        <script type="text/javascript">
            const vm = new Vue({
                el: '#app',
                data: {

                },
                methods: {
                    showDiv() {
                        alert("DIV你好!");
                    },
                    showInfo(e) {
                        e.stopPropagation();
                        alert("同学你好!");
                    }
                }
            })
        </script>

此时点button的话,他包裹的div也会触发时间showDiv,这就是冒泡

使用vue

@click.stop="showInfo"

事件只触发一次once

@click.once="showInfo"

事件捕获
capture

        <div @click.capture="showDiv">
            <button @click.stop="showInfo">点我</button>

        </div>

正常来说,是先捕获,再冒泡
也就是 先div,再button
如果使用.capture的话,就会先执行div,再执行button

    <div id="app">
        <div @click.capture="showDiv">
            <button @click="showInfo">点我</button>

        </div>

        <script type="text/javascript">
            const vm = new Vue({
                el: '#app',
                data: {

                },
                methods: {
                    showDiv() {
                        alert("DIV你好!");
                    },
                    showInfo() {
                        alert("同学你好!");
                    }
                }
            })
        </script>



事件当前操作元素才触发self

如上也是,只有当我们点击的是div的时候,才触发事件,点button冒泡过去的话,是不会触发事件的

        <div @click.self="showDiv">
            <button @click="showInfo">点我</button>

        </div>

这个方法也可以防止冒泡

passive:事件的默认行为立即执行,无需等待事件回调执行完毕:

有一些@事件,他会先去处理方法,如果方法里边耗时很久的话,不执行默认行为就会有卡顿,所以passvie可以解决这个问题

如果需要多个事件处理
@click.prevent.stop
顺序无所谓

键盘事件

keydown,按下去就触发事件

keyup,按下去,抬上来之后才出发事件

event:当前事件

event.target:当前事件操作的元素

event.target.value 这是这个例子中input的value

    <div id="app">
        <input type="text" @keyup.enter="showInfo">
    </div>

...方法
                showInfo(e) {
                    console.log(e.target.value);
                }

keyup.enter 的意思是,检测到回车才触发事件

常用的别名
Vue中常用的按键别名:
回车=>enter
删除=>delete(捕获“删除”和“退格”键)
退出=>esc
空格=>space
换行=>tab
上=>up
下=>down
左=>1eft
右=>right

计算属性

要用的属性不存在,需要自己计算得来
原理是: Object.defineProperty 提供的getter 和 setter

get方法调用的时机(1) 初次读取的时候 (2) 依赖的 数据发生变化的时候

和methods相比,优势是有缓存机制,效率高

计算属性在vm身上,直接调即可

<script type="text/javascript">
    new Vue(
        {
            el: '#root',
            data: {
                firstName: '',
                lastName: ''
            },
            computed: {
                fullname: {
                    get() {
                        return this.firstName + '-' + this.lastName
                    }
                }
            }
        }
    )
</script>

简写只有get方法的时候

            methods: {
                fullname(){
                    return this.firstName+this.lastName
                }
            }

set方法

set方法里边我们必须使得计算属性的依赖的数据发生改变,整体才会改变

    <div id="app">
        <input type="text" v-model="firstName"> <br>
        <input type="text" v-model="secondName">
        <div>{{fullName}}</div>

    </div>

    <script type="text/javascript">
        const vm = new Vue({
            el: '#app',
            data: {
                firstName: "",
                secondName: ""
            },
            computed: {
                fullName: {
                    get() {
                        return this.firstName + "-" + this.secondName
                    },
                    set(value) {
                        const arr = value.split("-");
                        this.firstName = arr[0];
                        this.secondName = arr[1];
                    }
                }
            }
        })
    </script>

如果没改变get方法中的firstName 和 secondName,计算属性走的还是缓存

监视属性

写法

    <div id="app">
        <div>今天天气{{info}}</div>

        <button @click="change">change</button>

    </div>

    <script type="text/javascript">
        const vm = new Vue({
            el: '#app',
            data: {
                isHot: false
            },
            computed: {
                info() {
                    return this.isHot ? '炎热' : '凉爽';
                }
            },
            methods: {
                change() {
                    this.isHot = !this.isHot;
                }
            },
            watch:{
                isHot: {
                    handler(newValue,oldValue) {
                        console.log(newValue,oldValue);
                    }
                }
            }
           
        })
    </script>

我们在watch里边直接写,isHot发生变化,就会执行handler里边的方法

另外一种写法

    <script type="text/javascript">
        const vm = new Vue({
            el: '#app',
            data: {
                isHot: false
            },
            computed: {
                info() {
                    return this.isHot ? '炎热' : '凉爽';
                }
            },
            methods: {
                change() {
                    this.isHot = !this.isHot;
                }
            }
        })

        vm.$watch('isHot',{
            handler(newValue,oldValue) {
                console.log(newValue,oldValue);
            }
        })
    </script>

在外边写也可以,只不过要注意的是,这里的isHot必须加引号

深度监视
如果我们要监视一个对象里边的数据的改变,需要加上配置项,deep:true

    <script type="text/javascript">
        const vm = new Vue({
            el: '#app',
            data: {
                numbers: {
                    a: 1,
                    b: 2
                }
            },
            watch: {
                numbers: {
                    immediate:true,
                    deep: true,
                    handler(newValue, oldValue) {
                        console.log('numbers发生变化',newValue,oldValue);
                    }
                }
            }
        })
    </script>

绑定样式

适用于class和style

最简单的写法

 <!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
    <div class="basic" :class="mood" @click="changeMood"></div>

数组的绑定
绑定class样式–数组写法,适用于:要绑定的样式个数不确定、名字也不确定

   <div class="basic" :class="['atguigu1','atguigu2']"></div>

对象的写法
适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用

<div class="basic" :class="{atguigu1:false, atguigu2:false}"></div>

简写
只有handler的时候,可以直接写

//原来的
watch: {
    numbers: {
        handler(newValue, oldValue) {
            console.log('numbers发生变化',newValue,oldValue);
        }
    }
}
//修改后的:
watch: {
    numbers(newValue, oldValue) {
            console.log('numbers发生变化',newValue,oldValue);
    }
}

computed 和 watch的区别
computed能实现的,watch都能实现,但是watch可以做异步操作

两个原则:

  1. 被vue管理的函数写成普通函数,this指向的是vm或者组件实例对象
  2. 所有不被vue所管理的函数,最好写成箭头函数,此时this指向的是vm或者组件实例对象

这里因为如果不被vue管理的函数写成普通函数,this指向的会是window

class 和 style样式

    <div id="root">
        <!-- 字符串写法,适用于:样式的类名不确定,需要动态指定 -->
        <div class="basic" :class="mood">{{number.a}}</div>

        <!-- 数组写法,适用于:要绑定的样式个数不确定,名字也不能确定 -->
        <div class="basic" :class="moodArr">{{number.b}}</div>

        <!-- 对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
        <div class="basic" :class="moodObj">{{number.b}}</div>

    </div>

...
data:{
               number:{
                a:1,
                b:2,
                
               },
               mood:'happy',
               moodArr:['happy'],
               moodObj:{
                happy:true
               }
            },

style的绑定和class的绑定一致

条件渲染

v-if,整个节点不在了
v-show 节点还在,只是隐藏

        <h1 v-if="n===1">1</h1>

        <h1 v-if="n===1">2</h1>

        <h1 v-if="n===1">3</h1>

如果条件一样,我们可以再外边写一个div

<div v-if="n===1">
        <h1>1</h1>

        <h1>2</h1>

        <h1>3</h1>

</div>

但是有可能会破坏结构,所以用template

<template v-if="n===1">
        <h1>1</h1>

        <h1>2</h1>

        <h1>3</h1>

</template>

template只能和v-if使用

列表渲染

v-for
      <!-- 遍历数组 -->
      在遍历数组时,数组元素后面的一个参数,是遍历时的key值,因为我们这里没有自定义key,所以默认是012
      <ul>
        <li v-for="(person,index) in persons">
          {{index}}--{{person.name}}--{{person.age}}
        </li>

      </ul>

      <!-- 遍历对象 -->
      遍历对象时,括号中第一个参数是对象中键值对的值,第二个参数是键值对的键,第三个参数是这个这条遍历的数据的key
      <ul>
        <li v-for="(value,key,index) in car">{{index}}--{{key}}--{{value}}</li>

      </ul>

      遍历字符串时,括号中第一个参数是字符串这个位置的值,第二个参数是这个这条遍历的数据的key
      <!-- 遍历字符串 -->
      <ul>
        <li v-for="(value,key) in str">{{key}}--{{value}}</li>

      </ul>

      

...
 data: {
          persons: [
            { id: "001", name: "ice", age: "13" },
            { id: "002", name: "peach", age: "12" },
          ],
          car: {
            speed: "20km/h",
            year: "2014",
          },
          str: "i am a word",
        },

key的原理和使用

key的内部原理

  1. 虚拟dom中key的作用:
    key是虚拟dom对象的标识,vue会根据新数据生成新的虚拟dom,随后进行diff算法
  2. 对比规则:
    (1) 旧的虚拟dom和新的虚拟dom,有相同key
    a. 如果虚拟dom中内容没变,则直接复用之前的真实dom
    b.如果内容变化,则生成新的dom
    (2)没有相同key,创建新的dom

看这个图就知道为什么index作为key,如果有逆序操作的时候,会出现界面问题了

两边新旧都有虚拟dom,此时进行diff比较,我们新的是逆序添加了老刘这条li

key相同都是0,input也相同,只有张三-18老刘-30不同,此时input相同

那么不同的内容,老刘-30会创建新的dom
对于相同的内容,input输入框,就会复用之前的dom,也就是原本应该是张三的输入框

以此类推,这里整个往上移,就会出现页面问题

解决办法也很简单,就是我们绑定key的时候,用可以唯一标识的key,比如id,身份证,手机号等等

如果不存在对数据的逆序,破坏顺序操作,用index也是没有问题的

列表过滤

实现列表过滤的效果

watch监视

    <div id="app">
        <label>姓名:</label><input v-model="name">
        <ul>
            <li v-for="(person) in persnsOfSearch">
                {{person.name}}--{{person.age}}
            </li>

        </ul>

    </div>

    <script type="text/javascript">
        const vm = new Vue({
            el: '#app',
            data: {
                name: '',
                persnsOfSearch: [],
                persons: [
                    { id: '001', name: 'ice', age: '17' },
                    { id: '002', name: 'ipeach', age: '18' },
                    { id: '003', name: 'icepeach', age: '19' }]
            },
            watch: {
                name: {
                    immediate: true,
                    handler(newValue, oldValue) {
                        console.log('输入发生变化');

                        this.persnsOfSearch = this.persons.filter((p) => {
                            return p.name.indexOf(newValue) != -1;
                        })
                    }
                }
            }
        })

    </script>

整体的写法就是
输入框的里边的值,我们监视他
如果发生变化,也就是传来新的值newValue,我们就去过滤数组,
过滤的条件是,在persons数组中,查找每个对象的name是否含有newValue

利用的是函数 str.indexOf(val) ,如果有就会返回位置,如果没有就会返回-1
所以只有 != -1,就说明含有该值,filter通过

最后我们把过滤好的数据传给persnsOfSearch ,不用原来的数组就是为了不破坏数据

计算属性实现

    <div id="app">
        <label>姓名:</label><input v-model="name">
        <ul>
            <li v-for="(person) in persnsOfSearch">
                {{person.name}}--{{person.age}}
            </li>

        </ul>

    </div>

    <script type="text/javascript">
        const vm = new Vue({
            el: '#app',
            data: {
                name: '',
                persons: [
                    { id: '001', name: 'ice', age: '17' },
                    { id: '002', name: 'ipeach', age: '18' },
                    { id: '003', name: 'icepeach', age: '19' }]
            },
            computed: {
                persnsOfSearch() {
                    return this.persons.filter((p) => {
                        return p.name.indexOf(this.name) != -1;
                    })
                }
            }
        })

    </script>

我们如果使用计算属性的话就可以快速的解决这个问题

第一,我们得监视name的改变,这里的监视name的改变,巧妙的把name写到计算属性里边了,计算属性里边,依赖的数据发生改变,那么计算属性也会发生改变

也就是我们这里的persnsOfSearch,里边依赖着this.name

第二,我们做过滤

还有一个好处就是,我们不用像watch那样还得写immediate:true,计算属性一开始就会去进行计算

排序


在上面的基础上,加上一个age过滤的功能

    <div id="app">
        <label>姓名:</label><input v-model="name">
        <ul>
            <li v-for="(person) in persnsOfSearch">
                {{person.name}}--{{person.age}}
            </li>

        </ul>

        <button @click="sortType = 0">原顺序</button>

        <button @click="sortType = 1">降序</button>

        <button @click="sortType = 2">升序</button>

    </div>

    <script type="text/javascript">
        const vm = new Vue({
            el: '#app',
            data: {
                name: '',
                persons: [
                    { id: '001', name: 'ice', age: '17' },
                    { id: '002', name: 'ipeach', age: '18' },
                    { id: '003', name: 'icepeach', age: '19' }],
                sortType: 0    
            },
            computed: {
                persnsOfSearch() {
                    const arr = this.persons.filter((p) => {
                        return p.name.indexOf(this.name) != -1;
                    })
                    //是否是原来的顺序
                    if(this.sortType != 0) {
                        arr.sort((p1,p2) => {
                            return this.sortType == '1' ? p2.age - p1.age : p1.age - p2.age;
                        }) 
                    } 
                    return arr;
                }
            }
        })

    </script>

修改对象和数组数据

新增属性

Vue中,如果是对象数据,新增属性,和删除属性,Vue是不会监测到的,所以如果要加的话,得借助this.$set(target,propertyName/index,value) 或者用Vue.set(target,propertyName/index,value)
向响应式对象中添加响应式的属性

注意对象不能是Vue实例,或者Vue实例的跟数据对象

删除属性

删除属性和新增用法差不多
this.$delete(target,propertyName/index,value) 或者用Vue.delete(target,propertyName/index,value)

修改数组数据

要不使用数组的方法,数组的方法都集成了响应式,或者还是用
this.$set(数组,索引,修改的数据)

获取表单数据

<input type="text"/>v-model收集的是value值,用户输入的就是value值
<input type="radio"/> v-model收集的是value值,我们必须给标签设置value值
<input type="checkbox"/>

  1. 如果没有设置value值,收集的是checked值(布尔)
  2. 如果设置了value值
    (1)v-model的初始值不是数组,收集的就是checked
    (2)v-model的初始值是数组,收集的就是value组成的数组

备注: v-model的三个修饰符
lazy: 失去焦点的再收集数据
number: 输入字符串转为有效数字
trim: 去掉首尾空格

生命周期

beforeCreate 是数据代理监测和数据代理创建之前,不是vm

beforeMount 此时只有虚拟dom,所以此时没有把虚拟dom转换为真实dom

beforeUpdate 此时数据是最新的,但是页面是旧的

beforedestroy 此时数据,方法都在,但是此时如果修改数据,方法是不会触发数据更新流程
一般这个时候,清除定时器,解绑自定义事件,取消订阅等等

注意
销毁之后,只是vm没了,但是真实dom还是存在的,也就是vm的工作成果还在,不过管理的他的vue死了

这里的解绑自定义事件是需要关注的,我们在destroy之后是不能触发自定义事件的

$nextTick()

等待下一次 DOM 更新刷新的工具方法

function nextTick(callback?: () => void): Promise<void>

里边的是回调函数,也就是在下次刷新之后的会执行的函数

插槽

默认插槽
<!-- 父组件中: -->
        <Category>
           <div>html结构1</div>
        </Category>
// 子组件中:
        <template>
            <div>
               <!-- 定义插槽 -->
               <slot>插槽默认内容...</slot>
            </div>
        </template>


具名插槽
<!-- 父组件中: -->
        <Category>
            <template slot="center">
              <div>html结构1</div>
            </template>

            <template v-slot:footer>
               <div>html结构2</div>
            </template>
        </Category>
// 子组件中:
        <template>
            <div>
               <!-- 定义插槽 -->
               <slot name="center">插槽默认内容...</slot>
               <slot name="footer">插槽默认内容...</slot>
            </div>
        </template>


v-slot:footer 是新的语法

作用域插槽
<!-- 父组件中: -->
      <Category>
         <template scope="scopeData">
            <!-- 生成的是ul列表 -->
            <ul>
               <li v-for="g in scopeData.games" :key="g">{{g}}</li>
            </ul>
         </template>
      </Category>

      <Category>
         <template slot-scope="scopeData">
            <!-- 生成的是h4标题 -->
            <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
         </template>
      </Category>

        
// 子组件中:
        <template>
            <div>
                <slot :games="games"></slot>
            </div>
        </template>
      
        <script>
            export default {
                name:'Category',
                props:['title'],
                //数据在子组件自身
                data() {
                    return {
                        games:['红色警戒','穿越火线','劲舞团','超级玛丽']
                    }
                },
            }
        </script>


slot-scope 是新语法

应用场景,数据在子组件,数据的生成的结构由组件的使用者决定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

憨憨小江

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值