Vue@2.X --- (搭建vue实例,选项配置,代理属性和方法)

124 阅读3分钟

文件目录树:

image.png

引入方式:

    1. 开发版本--全局暴露vue的构造函数
    • <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  • 或者npm模块下载2.0版本
    • npm i vue@2

导入

在src目录下新建index.js 导入vue模块

impot Vue from 'vue/dist/vue'

创建vue实例

到这里我们就可以在当前文件下创建一个vue实例

   const vm = new Vue({
       // 元素--表示在根目录哪个区域归我所管
       el:'#app',
       // 模板--挂载后会替换掉同位置原有标签,重新渲染
       template:'<h1 v-on:click="foo">{{c}}</h1>',
       // 数据源
       data(){
           return {
               a:1,
               b:[1,2,3]
           }
       },
       // 方法 -- 包含一些节点事件,业务逻辑等等
       methods:{
           foo(){
               console.log('v-on的打印事件')
           }
       }
   })

与此同时,我们还应该在根目录html文件中新建:

 <div id="app">
        <h1>{{b}}</h1>
 </div>

id为app就是我们在上面实例中el里的挂载点,且实例创建完成后,template模板会替换掉index.html里id为app节点下面的标签,重新渲染页面

Vue的选项配置 el template $mount

三者存在一定的依赖关系,具体大致分为五种情况:

  • ① : 如果el + template 都存在
    • 先编译template => html片段 => (el)替换掉提供的挂载目标节点
  • ② :如果只存在el 不存在template
    • 将获取到el.outerHTML的字符串 自动作为template属性 继续①的流程
  • ③ : 两者都不存在
    • 不会编译,也不会渲染
  • ④ :补救措施:
    • 利用vm.$mount('#app')手动地挂载一个未挂载的实例。这个方法返回实例自身,因而可以链式调用其它实例方法。
  • ⑤ : 如果在使用$mount()时未添加节点(ElementOrSelector : 元素选择器),但也想把节点渲染到页面中,
    • 模板将被渲染为文档之外的的元素,并且你必须使用原生 DOM API 把它插入文档中。
    document.body.replaceChild(vm.$el,要替换的当前节点);
    

具体代码实现如下:

//第一种:el + template都存在
const vm1 = new Vue({
  el: '#app',
  template: '<h1>{{msg}}</h1>',
  data() {
    return {
      msg: 'el + template',
    };
  },
});


//第二种:不存在el,实现$mount(#app)手动挂载
const vm2 = new Vue({
  template: '<h1>{{msg}}</h1>',
  data() {
    return {
      msg: 'template + $mount(el)',
    };
  },
}).$mount('#app');


//第三种:写了$monunt 但没有标明具体的挂载点,则利用原生Dom节点手动替换
const vm3 = new Vue({
  template: '<h1>{{msg}}</h1>',
  data() {
    return {
      msg: 'template + $mount + document.body.appChild(vm.$el)',
    };
  },
}).$mount();
document.body.replaceChild(vm.$el,要替换的当前节点);


//第四种:只存在el 不存在template 将获取到el.outerHTML的字符串 自动作为template属性 继续①的流程
const vm4 = new Vue({
  el: '#app',
  data() {
    return {
      msg: 'el + el.outerHTML as template',
    };
  },
});

代理 data 和 methods

原理:

1.什么是数据代理

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

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter

就比如你定义了一个对象a和一个对象b,你可以用b里面的属性来修改a里面的属性,这个是响应式的,那么这个究竟是怎么做到的呢

2.原理

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

Object.defineProperty(obj, prop, descriptor)
复制代码
  • 其中obj是你要要定义属性的对象
  • prop是你要定义的属性
  • desciptor 是要定义或修改的属性描述符

通过这个方法添加的属性默认情况下是不可以被枚举的

如果你想要其中的属性可以被枚举的话,就可以使用以下配置

    Object.defineProperty(a,'name',{
        value:'badspider',
        enumerable: true
    })
复制代码

那么vue中的数据代理就是依据这个方法的getter和setter

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更

    let a = {
        name:"badspider"
    }

    let b = {}

    object.defineProperty(b,'name',{
            get(){
                    console.log('我被调用了')
                    return  a.name
        }
            set(value){
                    console.log('我被修改了')
                    return a.name = value
            }
    })
复制代码

当你需要读取这个里面的数据的时候,get函数(getter)就会被调用,将其中的数据给你,当你修改数据的时候,set函数(setter)就会被调用来修改data中的值

image.png

vue中的具体底层代码实现:

function proxyProperty(option){
// option 代表传进来的实例
    if(!option.data) return data;
    if(typeof option.data === 'function'){
        //如果是有一个函数就将执行后的结果赋值给option.$data
        option.$data = option.data()
    };
    if(typeof option.data === 'object'){
        //如果是一个对象,则直接赋值
        option.$data = option.data;
    };
    
    //此时打印Object.entries(option.$data)得到一个二维键值对的数组
    console.log(Object.entries(option.$data));
    
    //代理data
    Object.entries(option.$data).forEach([key]=>{
        Object.defineProperty(option.$data,key,{
            get(){
                return option.$data[key]
            };
            set(value){
                option.$data[key] = value
            }
        })
    })
    
    Object.entries(option.methods || {} ).forEach([key]=>{
        Object.defineProperty(option.methods,key,{
            get(){
                 // 修改this指向 => 实例对象
                return option.methods[key].bind(option)
            };
            set(value){
                // 添加判断是否为函数
                if(typeof value !== 'function')return
                option.methods[key] = value
            }
        })
    })
    //返回实例对象
    return option
}