什么是Vue
Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,高效地开发用户界面。
Vue 的两个核心功能:
- 声明式渲染:Vue 基于标准 HTML 拓展了一套模板语法,使得我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系。
- 响应性:Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM。
Vue 是渐进式框架,可以自底向上逐层的应用。
基础语法
模板语法
插值语法
{{xxxx}},其中xxxx会作为js表达式运行
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue_test01</title>
</head>
<body>
    <!-- vue 操作此dom 此元素里面的后代元素全部可以被vue操作控制 -->
    <div id="root">
      <!-- 插值表达式语法:{{js表达式}} 表达式中的变量来自于data函数的返回值 -->
      <h1>你好{{name}}</h1>
      <h1>1+1={{1+20}}</h1>
    </div>
    <!-- CDN引入方式 -->
    <!-- <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> -->
    <!-- 引入本地vue.js文件 -->
    <script src="./vue.js"></script>
    <script>
     // 初始化 vue 对象
      const app = Vue.createApp({
        data(){  // 这里存放页面需要引用的数据,通过return返回对象
          // data这里定义数据
          return {
            name:'AiLynn',
          }
        }
      })
    app.mount('#root') // 挂载到id=root的元素上,开始操控此元素
    </script>
</body>
</html>指令语法
v-开头
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue_test01</title>
</head>
<body>
    <!-- vue 操作此dom 此元素里面的后代元素全部可以被vue操作控制 -->
    <div id="root">
      <!-- 插值表达式语法:{{js表达式}} 表达式中的变量来自于data函数的返回值 -->
      <h1>你好{{name}}</h1>
      <h1>1+1={{1+20}}</h1>
      <!-- 指令语法 v-指令 -->
      <a v-bind:href="vue3link">点我跳转到vue官网</a>
    </div>
    <!-- CDN引入方式 -->
    <!-- <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> -->
    <!-- 引入本地vue.js文件 -->
    <script src="./vue.js"></script>
    <script>
     // 初始化 vue 对象
      const app = Vue.createApp({
        data(){  // 这里存放页面需要引用的数据,通过return返回对象
          // data这里定义数据
          return {
            name:'AiLynn',
            vue3link:'https://cn.vuejs.org/'
          }
        }
      })
    app.mount('#root') // 挂载到id=root的元素上,开始操控此元素
    </script>
</body>
</html> 
数据绑定
单向绑定
v-bind 单向数据绑定
- 语法:v-bind:herf="xxx"或简写为:href
- 特点:数据只能从js流向html
双向绑定
v-model 双向数据绑定
- 语法:v-model="xxxx"
- 特点:数据不仅能从js流向html,还能从html流向js
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue_test02</title>
</head>
<body>
    <div id="root">
        <!-- v-bind:待绑定的属性="data返回的变量" -->
        <!-- 单向绑定,数据只能从js流向html -->
        <input type="text" placeholder="请输入姓名" v-bind:value="name">
        <!-- 上面的v-bind:value="name"可以简写为 :value="name" -->
        <!-- v-model="值" 自动绑定元素对应的属性(文本框、下拉框等) -->
        <!-- 双向绑定,数据可以从js流向html,也可以从html流向js -->
        <!-- <input type="text" placeholder="请输入年龄" v-model="age"> -->
        <!-- v-model的lazy模式,当脱离焦点后再进行同步 -->
        <input type="text" placeholder="请输入年龄" v-model.lazy="age">
        <h3>姓名:{{name}}</h3>
        <h3>年龄:{{age}}</h3>
    </div>
    <script src="./vue.js"></script>
    <script>
      Vue.createApp({
        data(){
            return{
                name:"AiLynn",
                age:18
            }
        }
      }).mount("#root")
    </script>
</body>
</html> 
v-model的补充说明
- v-model的lazy模式,当脱离焦点后再进行同步 - <input type="text" placeholder="请输入年龄" v-model.lazy="age">
- number修饰符,会将v-model绑定的数据转成number - <input type="text" placeholder="请输入年龄" v-model.number="age">
- trim修饰符,可以自动过滤用户的首尾空白字符串 - <input type="text" placeholder="请输入年龄" v-model.trim="age">
事件监听
语法:v-on:事件名
简写语法 @事件名,如@click
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue_test03</title>
</head>
<body>
    <div id="root">
        <!-- v-on:事件="js表达式" 简写为@事件="js表达式" -->
        <button v-on:click="age++">点我加1岁</button>
        <h3>年龄:{{age}}</h3>
        <!-- 加法计数器 -->
        <input type="text" placeholder="请输入第1个值" v-model.number="value1">
        <input type="text" placeholder="请输入第2个值" v-model.number="value2">
        <button @click="add">计算</button>
        <h3>结果:{{res}}</h3>
        <!-- 函数不加括号, vue会默认传递事件对象给函数 -->
        <input type="text" placeholder="请输入用户名" v-model="username" @keyup.enter="checkName">
        <!-- vue通过 $event 表示事件对象参数 -->
        <input type="text" placeholder="请输入用户名" v-model="username" @keyup.enter="checkName1('AiLynn',$event)">
        <h3>{{username}}</h3>
    </div>
    <script src="./vue.js"></script>
    <script>
        Vue.createApp({
            // 选项式API
            data(){
                return{
                    name: "AiLynn",
                    age: 18,
                    value1: "",
                    value2: "",
                    res: "",
                    username: ""
                }
            },
            methods:{   // 自定义定义方法
                // data定义的数据会绑定到this
                add(){
                    this.res = this.value1 + this.value2
                },
                checkName(){
                    console.log("触发了checkName")
                    console.log(event.target.tagName)   // 事件对象目标元素的标签名
                },
                checkName1(name,event){
                    console.log('检查:',name)
                    console.log(event.target.tagName)   // 事件对象目标元素的标签名
                }
            }
        }).mount("#root")
    </script>
</body>
</html>条件渲染
v-if,后面可以跟v-else或v-else-if形成多条件分支
v-show,与v-if用法相同,区别是v-if销毁创建dom,而v-show只是更改display样式。即v-if的资源开销要比v-show要大
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue_test04</title>
</head>
<body>
    <div id="root">
        <button @click="ate=!ate">开饭/消化</button>
        <!-- v-if 用于控制元素是否显示 控制元素的生成和销毁-->
        <!-- <h3 v-if="ate">吃过了</h3> -->
        <!-- v-show 作用是控制了元素的样式 (v-if 性能开销比v-show小,在元素频繁切换的场景建议使用v-show)-->
        <h3 v-show="ate">吃过了</h3>
        <h3 v-else>饿了</h3>
        <!-- v-if 后面紧跟着 v-else 或 v-else-if 可以实现分支效果 -->
    </div>
    <script src="./vue.js"></script>
    <script>
        Vue.createApp({
            data(){
                return{
                    ate: false,
                }
            },
            methods:{
            }
        }).mount("#root")
    </script>
</body>
</html>列表渲染
v-for,用于渲染列表。
语法:v-for="item in items" 用在需要多次渲染的元素上。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue_test05</title>
</head>
<body>
    <div id="root">
        <h3>待办事项:</h3>
        <ul>
            <!-- v-for 用于需要循环渲染的元素上,常用于列表元素 -->
            <!-- v-for 语法 v-for="item in itemlist" -->
            <li v-for="todo in todo_list">{{todo}}</li>
        </ul>
    </div>
    <script src="./vue.js"></script>
    <script>
        Vue.createApp({
            data(){
                return{
                    todo_list: ["吃饭","睡觉","打豆豆"]
                }
            }
        }).mount("#root")
    </script>
</body>
</html>MVVM模型
M Model:数据层,即data()返回的数据
V View:视图,data()挂载的html的内容
VM ViewModel:视图模型,主要用于Model和View之间的桥梁,包含DOM Listeners和Data Bindings,通过数据的双向绑定进行关联
 
响应式原理
什么是响应式?
响应式就是视图渲染时使用到了一个数据,当数据更新时,视图就会响应是否更新。
在 Vue2 中,Vue 是使用 Object.defineProperty 来实现响应式的。
在 Vue 3 中,数据是基于 JavaScript Proxy(代理) 实现响应式的。通过Proxy(代理)拦截对象中任意属性的变化,包括属性值的读写、属性的增加、属性的删除等;通过Reffect(反射)对源对象的属性进行操作。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue_test06</title>
</head>
<body>
    <script>
        // proxy 是ES6引入的API,作用是在目标对象之前拦截一层代理,可以通过操作代理实现操作对象。
        const obj = {
            addr:"北京"
        }
        // 创建代理obj
        const p = new Proxy(obj,{
            // 拦截取值动作
            get: function(target,key){  // 目标对象,属性
                console.log("拦截get操作--vue这里做了更复杂的操作")
                // console.log(target)
                // console.log(key)
                // return target[key]
                return Reflect.get(target,key)  // 反射的方式取值
            },
            // 拦截设置值的动作
            set: function(target,key,value){
                console.log('拦截set操作--vue这里做了更复杂的操作')
                target[key]=value
                Reflect.set(target,key,value)
            }
        })
    </script>
</body>
</html> 
选项式API扩展知识
computed
computed 计算属性——用于简化模板里的逻辑
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>vue_test07</title>
</head>
<body>
  <div id="root">
    <h3>{{chilema}}</h3>
    <button @click="ate=!ate">吃饭/消化</button>
  </div>
  <script src="./vue.js"></script>
  <script>
    const vm = Vue.createApp({
      data(){
        return{
          ate: false
        }
      },
      computed:{  // 计算属性——用于简化模板里的逻辑
        chilema(){  // 定义方法——模板里面当做属性来用
          return this.ate?"吃过了":"没吃呢"
        }
      }
      // computed与methods的区别:
      // computed会缓存结果(当操作的数据没有发生改变时),
      // 而methods会立即执行,不缓存结果。
    }).mount("#root")
  </script>
</body>
</html>watch
watch监听器,可以检测数据的变化,执行一些复杂逻辑
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>vue_test08</title>
</head>
<body>
  <!-- 监听器的作用:可以检测数据的变化,执行一些复杂逻辑 -->
  <div id="root">
    <input type="text" placeholder="请输入用户名" v-model="username">
    <h3>判断结果:{{result}}</h3>
    <input type="password" placeholder="请输入密码" v-model="password">
    <input type="password" placeholder="请再次输入密码" v-model="password_repeat">
    <h3>{{res}}</h3>
  </div>
  <script src="./vue.js"></script>
  <script>
    Vue.createApp({
      data(){
        return{
          username:'',
          result:'',
          password:'',
          password_repeat:'',
          res:''
        }
      },
      watch:{
        // 监听器语法
        // 监听的变量名(new变量名,old变量名){判断,如果新变量名xxxx,就xxxx}
        username(newname,oldname){
          console.log('newname:',newname)
          console.log('oldname:',oldname)
          if(newname.length<8){
            this.result='用户名不合法'
          }else{
            this.result="合法"
          }
        },
        password_repeat(newvalue,oldvalue){
          if(newvalue !=this.password){
            this.res='两次密码输入不一致'
          }else{
            this.res=''
          }
        }
      }
    }).mount("#root")
  </script>
</body>
</html> 
                        
                        