Vue _ 教程版 01 基础

57 篇文章 4 订阅

目录

目标

一、Vue 基础

1.1、介绍

1.2、声明式渲染和组件化

1.3、MVVM 模式

二、Vue 基础使用

2.1、传统 Dom 操作

2.2、使用 Vue 实现

2.3、vue devtools 工具安装

2.4、Vue 实现数据绑定的原理

图例 :


跳转链接 =>  Vue _ 教程版 02 指令

跳转链接 =>  Vue _ 教程版 03

跳转链接 =>  Vue _ 教程版 04 组件

跳转链接 =>  Vue _ 教程版 05


目标

  • 在网页中实例化 Vue 对象
  • 知道 Vue 数据绑定原理
  • 熟练使用 插值表示式

一、Vue 基础

1.1、介绍

官网:Vue.js

Vue ( 读音 /vjuː/,类似于 view )  , Vue.js 是一套构建用户 界面 渐进式框架 。Vue 采用自底向上增量开发的设计。Vue 的核心库 只关注 视图层 ,它不仅易于上手,还便于与第三方库 或 既有项目整合。另一方面,当与 现代化的工具链 以及各种 支持类库 结合使用时,Vue 也完全能够为复杂的 单页应用程序提供驱动。( spa 单页面应用,所有的显示都在一个页面当中)

渐进式:一步一步,不是说你必须一次把所有的东西都用上

自底向上设计:是一种设计程序的过程和方法,就是先编写出基础程序段,然后再逐步扩大规模、补充和升级某些功能,实际上是一种自底向上构造程序的过程。

Vue 从设计角度来讲,虽然能够涵盖这张图上所有的东西,但是你并不需要一上手就把所有东西全用上,都是可选的。声明式渲染系统 是 Vue 的 核心库 所包含内容,而 路由、状态管理、构建工具 都有专门 解决方案 。这些解决方案相互独立,你可以在核心的基础上任意选用其他的部件,不一定要全部整合在一起。

1.2、声明式渲染和组件化

  • 声明式渲染

Vue.js 的核心是一个允许采用简洁的 模板语法 来 声明式的将数据渲染进 DOM 的系统

  • 组件化应用构建

组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。几乎任意类型的应用界面都可以抽象为一个 组件树

1.3、MVVM 模式

MVVM 是 Model-View-ViewModel 的简写。它本质上就是 MVC 的 改进版 。MVVM 就是将其中的View 的 状态 和 行为 抽象化 ,让我们将 视图 UI业务逻辑 分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。

Vue 使用 MVVM 响应式编程模型 ,避免直接操作 DOM , 降低 DOM 操作的 复杂性 。

MVVM:页面输入改变数据,数据改变 影响 页面数据 展示与渲染

M(model):普通的 javascript 数据对象

V(view):前端展示 页面

VM(ViewModel):用于双向绑定数据与页面,对于我们的课程来说,就是 Vue 的实例


二、Vue 基础使用

2.1、传统 Dom 操作

使用 js 对 html 页面结构中的指定的区域输出数据

传统 dom 操作数据显示 : 

<div>
    <h3 id="app">abcd</h3>
    <input type="text" id="msg">
  </div>

  <script>
    var data = {
      username: '张三'
    }
    document.querySelector('#app').innerHTML = data.username
    // document.querySelector('#app').textContent = data.username
    // 查看当前dom的类型 1元素 3文本
    // console.log(document.querySelector('#app').nodeType)
    // console.log(document.querySelector('#app').firstChild.nodeType)
    document.querySelector('#msg').value = data.username
    document.querySelector('#msg').addEventListener('input', function () {
      // 让使用数据源的标签更新视图
      document.querySelector('#app').innerHTML = this.value.trim()
      // 更新数据源
      data.username = this.value.trim()
    })
  </script>

 

2.2、使用 Vue 实现

在 html 页面中使用 Vue 需要完成如下步骤即可

下载 Vue.js

介绍 — Vue.js

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue _ 数据双向绑定</title>
</head>
<body>
    <!-- 下面这是能在界面看见的部分就是 视图 部分 -->
    <!-- 被 Vue 管理 -->
    <div id="app">
        <!-- {{ 双花括号之间不能有空格 , 需要紧挨在一起 }} -->
        <h1> {{message}} </h1>
        <h2>{{name}}</h2>
    </div>
    <!-- 不被 Vue 管理 -->
    <div>{{message}}</div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <script>
        /* 
        Vue 最大的特点 : 数据双向绑定
        这个双向绑定指的是谁和谁绑定的呢?? => 数据 和 视图
( 操作 数据 , DOM 也发生了变化 , input 输入框把 数据 告诉 app.message 这个属性
(由 Vue 的实例 , 将上面的这一个 h1 进行了修改) )
        1. DOM 将数据传递给 Vue 实例
        2. Vue 实例的属性会传递给 DOM
        */
        // let(变量) / const(常量)
// 编程范式 : 声明式编程 ( 当我这个实例去帮我管理这个 div 的时候 , 
//你只需要告诉我这里写的什么东西就可以了 , 你只需要声明我这里需要显示什么东西就可以了 , 
至于它内部如何处理的 , 无需知晓 )
// 它的好处 : 可以真正做到数据和我们的界面完全分离 , 以后并不需要再通过代码去创建元素了
        // 创建 Vue 实例
        const app = new Vue({
            // vue 实例的配置项:
            // element , vue 实例控制这一块 dom
            el: '#app', // 用于挂载要管理的元素
            // vue 实例的属性
            // data 内部的属性和属性值就是 vue 实例的属性和属性值
            // 下面 data 这一部分就是 数据
            data: { // 定义数据
                message: "hello Vue",
                name: '小灰狼'
            }
        });
        console.log(app.message);
        // 响应式: 就是当数据发生改变的时候, 界面会自动发生一些响应, 会跟着自动更改
        // 原始 js 的做法 : ( 编程范式 : 命令式编程 )
        // =>( 就是一步一步告诉你怎么做, 需要你每一步都指定的非常清除 , 他才知道怎么做 )
        // 1. 创建 div 元素 , 设置 id 属性
        // 2. 定义一个变量叫 message
        // 3. 将 message 变量放在前面的 div 元素中显示
        // 4. 修改 message 的数据: 今天下雨天气 !
        // 5. 将修改后的数据再次替换到 div 元素
    </script>
</body>
</html>

2.3、vue devtools 工具安装

通过 Chrome 中的谷歌插件商店安装 Vue Devtools 工具,此工具帮助我们进行 vue 数据调试所用,一定要安装。

https://chrome.google.com/webstore?utm_source=chrome-ntp-icon

极简插件_Chrome扩展插件商店_优质crx应用

  • 在 VsCode 中安装插件

2.4、Vue 实现数据绑定的原理

当把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的 属性,使用 Object.defineProperty ( Vue2.x 版本) ,  Vue3.x 中使用了 Proxy 类 把这些属性全部转为 getter / setter ( 数据劫持 ) 。在 属性被访问和修改时 通知变化 。每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

vue 实现数据响应式

<body>
  <h3 v-html="msg"></h3>
  <input type="text" v-model="msg">
  <script>
    // 初始化数据 -- vue优化,扁平化数据
    let data = {
      msg: 'hello',
      user: { id: 100 }
    }
    // 劫持或代理
	// 观察者
    observer(data)
	
    // 编译 模板
    compileTmeplate(data)
    function compileTmeplate(target) {
      /* document.querySelectorAll('[v-model]').forEach(node => {
        console.log(node)
      }) */
      document.querySelectorAll('*').forEach(node => {
        [...node.attributes].forEach(attr => {
          if (/^v-/.test(attr.name)) { // 双向绑定所要用到的属性
            if (attr.name === 'v-model') {
              node.value = target[attr.nodeValue]
              // 表单项,事件和数据绑定
              node.addEventListener('input', function () {
                // 更新数据
                target[attr.nodeValue] = this.value.trim()
              })
            } else {
              node.innerHTML = target[attr.nodeValue]
            }
          }
        })
      })
    }

    function observer(target) {
      if (Object.prototype.toString.call(target) != '[object Object]') return;
      for (let key in target) {
        defineReactive(target, key, target[key])
      }
    }

    // 代理
    function defineReactive(target, key, value) {
      observer(value)
      Object.defineProperty(target, key, {
        get() {
          console.log('get')
          return value
        },
        set(val) {
          console.log('set')
          if (val != value) {
            document.querySelectorAll('[v-html]').forEach(node => {
              node.innerHTML = val
            })
            document.querySelectorAll('[v-model]').forEach(node => {
              node.value = val
            })
            value = val
          }
        }
      })
    }
  </script>
</body>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vue实现数据绑定的原理</title>
</head>
<body>
  <div id="root">
        <h3 v-text="title"></h3>
    <hr>
        <input type="text" v-model='title'>
  </div>
  <script>
    // 数据劫持
    let data = {
      title: '我是一个标题',
    };
    // 观察数据
    observe(data)
    // 给input绑定事件
    document.querySelector('[v-model]').addEventListener('input', function () {
      let key = this.getAttribute('v-model')
      data[key] = this.value.trim()
    })
    function observe(target) {
      if (!isObject(target)) return;
      for (let key in target) {
        defineReactive(target, key, target[key])
      }
    }
    // 数据劫持
    function defineReactive(target, key, value) {
      Object.defineProperty(target, key, {
        get() {
          console.log('get')
          return value
        },
        set(v) {
          if (v != value) {
            value = v
            console.log('set')
            // 更新视图
            updateView(value, key)
          }
        }
      })
    }
    function updateView(value, key) {
      document.querySelectorAll('[v-text]').forEach(node => {
        let attrValue = node.getAttribute('v-text')
        if (key === attrValue) {
          if (node.nodeName === 'INPUT') {
            node.value = value;
          } else {
            node.innerHTML = value;
          }
        }
      })
    }
    function isObject(target) {
      // return target !== null && typeof target === 'object' && !(target instanceof Array)
      // return Object.prototype.toString.call(target) === '[object Object]'
      return ({}).toString.call(target) === '[object Object]'
    }
  </script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>数据劫持</title>
</head>
<body>
  <div id="app">
    <h3 v-text="msg"></h3>
    <h3 v-text="age"></h3>
    <hr>
    <input type="text" v-model="msg">
  </div>
  <script>
    var data = {
      msg: '你好mvvm',
      age: 20,
      user: { id: 1 },
      user_id: 1,
      user_name: 'aa'
    }
    
    // let target = { id: 100 }
    // 冻结对象 对象就没有办法修改了
    // target = Object.freeze(target)
    // target.id = 200
    // let target = {}

    // 数据劫持
    observe(data)
    // 模板编译
    compileRender(data)

    function observe(target) {
      // 只劫持json对象
      if (Object.prototype.toString.call(target) != '[object Object]') return;
      // 遍历
      for (let key in target) {
        defineRactive(target, key, target[key])
      }
    }

    function defineRactive(target, key, value) {
      observe(value)
      /* if (Object.prototype.toString.call(value) == '[object Object]') {
        observe(value)
        return;
      } */
      // 劫持当前的对象
      // defineProperty 它只能对对象中的属性进行劫持,不能劫持数组
      Object.defineProperty(target, key, {
        // 获取器
        get() {
          console.log('get')
          return value
        },
        // 修改器
        set(newV) {
          if (newV != value) {
            console.log('set')
            value = newV
            // 通知模板编译一下
            compileRender(target)
          }
        }
      });
    }

    function compileRender(target) {
      // 模板编译
      // 文本
      document.querySelectorAll(`[v-text]`).forEach(node => {
        // 要去数据源中查找的数据key
        let key = node.getAttribute('v-text')
        let value = target[key] || 0
        node.innerHTML = value
      })
      // 输入框
      document.querySelectorAll(`[v-model]`).forEach(node => {
        let key = node.getAttribute('v-model')
        let value = target[key] || 0
        node.value = value
        // 绑定一个事件
        node.addEventListener('input', function () {
          target[key] = this.value.trim()
        })
      })
    }
  </script>
</body>
</html>

相关知识点 :  

let target = { id: 100 }
冻结对象 , 对象就没有办法修改了
target = Object.freeze( target )
target.id = 200 


Dom 操作在内存中完成
=> 文档碎片 --> 在内存在存储,不会在界面中渲染,通过 appendChild 渲染到视图中
语法 document.createDocumentFragment( ) 

作用 : 可以承载节点 , 当把文档碎片插入页面时 , 文档碎片不进入页面 , 只有文档碎片里面承载的内容进入页面 ( 往内存里写入东西比往页面里写入东西会更快 ) 


( 1 ) 数据劫持-跳转链接  =>  数据劫持 _ 简述版

( 2 ) 观察者模式-跳转链接  =>  JavaScript _ 设计模式


自己来实现一个 _ MVVM : 

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>实现一个 _ MVVM</title>
  <script src="./js/myVue.js"></script>
</head>
<body>
  <div id="app">
    <h3>{{ title }}</h3>
    <input type="text" v-model='title'>
  </div>
  <script>
    const vm = new Vue({
      el: '#app',
      data: {
        title: '你好MVVM'
      }
    })
  </script>
</body>
</html>
class Vue {
  constructor(options) {
    // 挂载点的 Dom
    this.$el = document.querySelector(options.el)
    // 数据
    this.$data = options.data
    // 数据劫持
    observe(this.$data)
    // 模板 编译
    compileTemplate(this.$el, this)
  }
}

// 观察仓库
class Dep {
  constructor() {
    // 观察者队列
    this.subscribes = []
  }
  // 添加观察者
  addSub(watcher) {
    this.subscribes.push(watcher)
  }
  // 通知更新
  notify() {
    this.subscribes.forEach(watcher => watcher.update())
  }
}
// 观察者
class Watcher {
  constructor(vm, key, node) {
    // 仓库对象的静态属性来保存当前 watcher 实例对象
    Dep.target = this;
    this.$vm = vm;
    this.$key = key;
    this.$node = node;
    // 触发数据劫持中的 get 方法,此时 Dep.target 的值为当前对象
    this.getValue();
    // 设置为 null ,为下次 new Watcher 准备
    Dep.target = null;
  }
  getValue() {
    // 触发 get
    this.$value = this.$vm.$data[this.$key]
  }
  update() {
    // 获取一下最新数据
    this.getValue();
    if (this.$node.nodeType === 1) {
      if (this.$node.nodeName == 'INPUT') {
        this.$node.value = this.$value
      } else {
        this.$node.innerHTML = this.$value
      }
    } else if (this.$node.nodeType === 3) {
      this.$node.textContent = this.$value
    }
  }
}

// 模板编译
function compileTemplate(el, vm) {
  // Dom 操作在内存中完成
  // 文档碎片 --> 在内存在存储,不会在界面中渲染,通过 appendChild 渲染到视图中
  let fragment = document.createDocumentFragment()
  let childNode;
  while (childNode = el.firstChild) {
    // 渲染模板
    compileRender(childNode, vm)
    // 把得到的 Dom 对象放到 fragment 中,此时 Dom 会删除
    fragment.appendChild(childNode)
  }
  // 处理完成后,放到视图中
  el.appendChild(fragment)
}
// 编译视图显示
function compileRender(node, vm) {
  // 判断当前节点的类型 1元素,3文本
  if (node.nodeType === 1) {
    // 得到元素所有的属性集合
    // [...node.attributes].forEach(attrObj => {
    // console.log(attrObj.name, attrObj.value);
    [...node.attributes].forEach(({ name, value }) => {
      // 只关心 v- 开头属性
      if (/^v-/.test(name)) {
        if (name === 'v-model') { // 针对于input输入框
          node.value = vm.$data[value]
          // 观察者
          new Watcher(vm, value, node)
          // 绑定事件
          node.addEventListener('input', function () {
            vm.$data[value] = this.value.trim()
          });
        } else {
          // 观察者
          new Watcher(vm, value, node)
          node.innerHTML = vm.$data[value]
        }
      }
    });
    // 问一下有没有子元素了
    node.childNodes.forEach(child => compileRender(child, vm))
  } else if (node.nodeType === 3) { // 文本节点
    // 内容
    let cnt = node.textContent
    // 匹配只有 {{}} 才进行处理
    let preg = /\{\{\s*(\w+)\s*\}\}/
    // 替换
    cnt = cnt.replace(preg, (a0, a1) => {
      // 观察者
      new Watcher(vm, a1, node)
      return vm.$data[a1]
    })
    node.textContent = cnt
  }
}

// ------------------- 数据劫持
// 监听数据源
function observe(target) {
  // 只劫持 json 对象
  if (Object.prototype.toString.call(target) != '[object Object]') return;
  // 遍历
  for (let key in target) {
    defineRactive(target, key, target[key])
  }
}
// 实现劫持
function defineRactive(target, key, value) {
  observe(value)
  let dep = new Dep()
  Object.defineProperty(target, key, {
    // 获取器
    get() {
      if (Dep.target) {
        // 添加了观察者到通知队列中
        dep.addSub(Dep.target)
      }
      return value
    },
    // 修改器
    set(newV) {
      if (newV != value) {
        value = newV
        dep.notify()
      }
    }
  });
}

图例 :


跳转链接 =>  Vue _ 教程版 02 指令

跳转链接 =>  Vue _ 教程版 03

跳转链接 =>  Vue _ 教程版 04 组件

跳转链接 =>  Vue _ 教程版 05

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值