vue2.x 对象劫持的原理实现


Posted in Javascript onApril 19, 2020

目标:手写迷你版Vue

一:使用rollup打包,打包后的代码体积更小,更适合写框架源码的打包

npm i rollup -D

二:安装babel相关的包,以及实现静态服务,设置环境变量的包

npm i @babel/core @babel/preset-env rollup-plugin-babel roullup-plugin-serve cross-env -D

三:包的相关介绍

  • rollup (打包工具)
  • @babel/core(用babel核心模块)
  • @babel/preset-env(babel将高级语法转成低级语法)
  • rollup-plugin-serve(实现静态服务)
  • cross-env(设置环境变量)
  • rollup-plugin-babel(桥梁)

四:根目录书写rollup.config.js

import babel from 'rollup-plugin-babel';
import serve from 'rollup-plugin-serve';
export default {
 input:'./src/index.js', // 以哪个文件作为打包的入口
 output:{
   file:'dist/umd/vue.js', // 出口路径
   name:'Vue', // 指定打包后全局变量的名字
   format: 'umd', // 统一模块规范
   sourcemap:true, // es6-> es5 开启源码调试 可以找到源代码的报错位置
 },
 plugins:[ // 使用的插件
   babel({
     exclude:"node_modules/**"
   }),
   process.env.ENV === 'development'?serve({
     open:true,
     openPage:'/public/index.html', // 默认打开html的路径
     port:3000,
     contentBase:''
   }):null
 ]
}

配置package.josn

{
 "name": "vue_souce",
 "version": "1.0.0",
 "description": "",
 "main": "index.js",
 "scripts": {
  "build:dev": "rollup -c",
  "serve": "cross-env ENV=development rollup -c -w"
 },
 "keywords": [],
 "author": "",
 "license": "ISC",
 "devDependencies": {
  "@babel/core": "^7.9.0",
  "@babel/preset-env": "^7.9.5",
  "cross-env": "^7.0.2",
  "rollup": "^2.6.1",
  "rollup-plugin-babel": "^4.4.0",
  "rollup-plugin-serve": "^1.0.1"
 }
}

五:新建index.html(public/index.html)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="/dist/umd/vue.js"></script>
  <script>
    let vm = new Vue({
      el:'#app',
      // 随便给些数据
      data(){
        return {
        name:'张三',
        age:11,
        address:{
          number:0,
          name:'李四'
        }}
      },
    })
    vm._data.address = {a:1};
    console.log(vm._data)
  </script>
</body>
</html>

六:编写Vue入口:index.js

// Vue的核心代码 只是Vue的一个声明
import {initMixin} from './init';
function Vue(options){
  // 进行Vue的初始化操作
  this._init(options);

}
// 通过引入文件的方式 给Vue原型上添加方法
initMixin(Vue); // 给Vue原型上添加一个_init方法
export default Vue

七:编写初始化操作 init.js

import {initState} from './state'
// 在原型上添加一个init方法
export function initMixin(Vue){
  // 初始化流程
  Vue.prototype._init = function (options) {
    // 数据的劫持
    const vm = this; // vue中使用 this.$options 指代的就是用户传递的属性
    vm.$options = options;

    // 初始化状态
    initState(vm); // 分割代码
  }
}

八:初始化数据

import {observe} from './observer/index.js'
export function initState(vm){
  const opts = vm.$options;
  // vue的数据来源 属性 方法 数据 计算属性 watch
  if(opts.props){
    initProps(vm);
  }
  if(opts.methods){
    initMethod(vm);
  }
  if(opts.data){
    initData(vm);
  }
  if(opts.computed){
    initComputed(vm);
  }
  if(opts.watch){
    initWatch(vm);
  }
}
function initProps(){}
function initMethod() {}
function initData(vm){
  // 数据初始化工作
  let data = vm.$options.data; // 用户传递的data
  data = vm._data = typeof data === 'function'?data.call(vm):data;
  // 对象劫持 用户改变了数据 我希望可以得到通知 =》 刷新页面
  // MVVM模式 数据变化可以驱动视图变化 
  // Object.defineProperty () 给属性增加get方法和set方法
  observe(data); // 响应式原理
}
function initComputed(){}
function initWatch(){}

九:书写核心监听功能

// 把data中的数据 都使用Object.defineProperty重新定义 es5
// Object.defineProperty 不能兼容ie8 及以下 vue2 无法兼容ie8版本
import {
  isObject
} from '../util/index'
// 后续我可以知道它是不是一个已经观察了的数据 __ob__
class Observer{
  constructor(value){ // 仅仅是初始化的操作
    // vue如果数据的层次过多 需要递归的去解析对象中的属性,依次增加set和get方法
    // 对数组监控
    this.walk(value); // 对对象进行观测
  }
  walk(data){
    let keys = Object.keys(data); // [name,age,address]

    // 如果这个data 不可配置 直接return
    keys.forEach((key)=>{
      defineReactive(data,key,data[key]);
    })
  }
}
function defineReactive(data,key,value){
  observe(value); // 递归实现深度检测
  Object.defineProperty(data,key,{
    configurable:true,
    enumerable:false,
    get(){ // 获取值的时候做一些操作
      return value;
    },
    set(newValue){ // 也可以做一些操作
      if(newValue === value) return;
      observe(newValue); // 继续劫持用户设置的值,因为有可能用户设置的值是一个对象
      value = newValue;
    }
  });
}

export function observe(data) {
  let isObj = isObject(data);
  if (!isObj) {
    return
  }
  return new Observer(data); // 用来观测数据
}

十:编写工具类文件,存放校验对象

/**
 * 
 * @param {*} data 当前数据是不是对象
 */
export function isObject(data) {
  return typeof data === 'object' && data !== null;
}

总结:

1 创建Vue构造函数,接收所有所有参数options
2 分类初始化options,本章主要处理data,让data上的引用类型的数据通过Object.definePrototy 变成响应式的,初始化是有循序的,先初始化props 然后初始化method 然后初始化data computed watch

3 核心如下

function defineReactive(data,key,value){
  observe(value); // 递归实现深度检测
  Object.defineProperty(data,key,{
    configurable:true,
    enumerable:false,
    get(){ // 获取值的时候做一些操作
      return value;
    },
    set(newValue){ // 也可以做一些操作
      if(newValue === value) return;
      observe(newValue); // 继续劫持用户设置的值,因为有可能用户设置的值是一个对象
      value = newValue;
    }
  });
}

到此这篇关于vue2.x 对象劫持的原理实现的文章就介绍到这了,更多相关vue2.x 对象劫持内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
jQuery.each()用法分享
Jul 31 Javascript
Three.js源码阅读笔记(物体是如何组织的)
Dec 27 Javascript
SeaJS 与 RequireJS 的差异对比
Dec 08 Javascript
JavaScript焦点事件、鼠标事件和滚轮事件使用详解
Jan 15 Javascript
基于jQuery实现数字滚动效果
Jan 16 Javascript
jQuery EasyUI开发技巧总结
Sep 26 jQuery
JavaScript学习总结(一) ECMAScript、BOM、DOM(核心、浏览器对象模型与文档对象模型)
Jan 07 Javascript
Vue render渲染时间戳转时间,时间转时间戳及渲染进度条效果
Jul 27 Javascript
js尾调用优化的实现
May 23 Javascript
基于JavaScript伪随机正态分布代码实例
Nov 07 Javascript
vue组件中实现嵌套子组件案例
Aug 31 Javascript
vue实力踩坑之push当前页无效
Apr 10 Vue.js
基于js判断浏览器是否支持webGL
Apr 18 #Javascript
JavaScript对象字面量和构造函数原理与用法详解
Apr 18 #Javascript
javascript 内存模型实例详解
Apr 18 #Javascript
javascript-hashchange事件和历史状态管理实例分析
Apr 18 #Javascript
javascript使用Blob对象实现的下载文件操作示例
Apr 18 #Javascript
原生js实现的观察者和订阅者模式简单示例
Apr 18 #Javascript
es6函数name属性功能与用法实例分析
Apr 18 #Javascript
You might like
php提示Call-time pass-by-reference has been deprecated in的解决方法[已测]
2012/05/06 PHP
php使用mysqli向数据库添加数据的方法
2015/03/20 PHP
php验证码生成器
2017/05/24 PHP
PHP实现找出有序数组中绝对值最小的数算法分析
2017/08/07 PHP
ASP中进行HTML数据及JS数据编码函数
2009/11/11 Javascript
关于Jquery操作Cookie取值错误的解决方法
2013/08/26 Javascript
JavaScript及jquey实现多个数组的合并操作
2014/09/06 Javascript
js console.log打印对像与数组用法详解
2016/01/21 Javascript
基于javascript数组实现图片轮播
2016/05/02 Javascript
JavaScript获取URL中参数querystring的方法详解
2016/10/11 Javascript
NodeJS中的MongoDB快速入门详细教程
2016/11/11 NodeJs
JS前端开发判断是否是手机端并跳转操作(小结)
2017/02/05 Javascript
jquery使用EasyUI Tree异步加载JSON数据(生成树)
2017/02/11 Javascript
Angular.js前台传list数组由后台spring MVC接收数组示例代码
2017/07/31 Javascript
JS严格模式知识点总结
2018/02/27 Javascript
js异步上传多张图片插件的使用方法
2018/10/22 Javascript
jQuery实现判断滚动条滚动到document底部的方法分析
2019/08/27 jQuery
对vue中的事件穿透与禁止穿透实例详解
2019/10/28 Javascript
React 实现车牌键盘的示例代码
2019/12/20 Javascript
vue动态设置页面title的方法实例
2020/08/23 Javascript
JavaScript中CreateTextFile函数
2020/08/30 Javascript
Python XML RPC服务器端和客户端实例
2014/11/22 Python
Python中subprocess的简单使用示例
2015/07/28 Python
Python多重继承的方法解析执行顺序实例分析
2018/05/26 Python
Python3.7 pyodbc完美配置访问access数据库
2019/10/03 Python
Python之指数与E记法的区别详解
2019/11/21 Python
pytorch中 gpu与gpu、gpu与cpu 在load时相互转化操作
2020/05/25 Python
tensorflow dataset.shuffle、dataset.batch、dataset.repeat顺序区别详解
2020/06/03 Python
NOTINO英国:在线购买美容和香水
2020/02/25 全球购物
酒店服务实习自我鉴定
2013/09/22 职场文书
学生会招新策划书
2014/02/14 职场文书
工程质量月活动方案
2014/02/19 职场文书
就业协议书怎么填
2014/04/11 职场文书
南湾猴岛导游词
2015/02/09 职场文书
大学生求职自荐信范文
2015/03/04 职场文书
学习师德师风的心得体会(2篇)
2019/10/08 职场文书