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 相关文章推荐
返回对象在当前级别中是第几个元素的实现代码
Jan 20 Javascript
javascript错误的认识不用关心内存管理
Dec 15 Javascript
node.js Web应用框架Express入门指南
May 28 Javascript
jquery实现键盘左右翻页特效
Apr 30 Javascript
JavaScript事件委托实例分析
May 26 Javascript
详解JavaScript中的blink()方法的使用
Jun 08 Javascript
JQuery中DOM实现事件移除的方法
Jun 13 Javascript
JS遍历页面所有对象属性及实现方法
Aug 01 Javascript
JavaScript实现图片懒加载(Lazyload)
Nov 28 Javascript
jquery.tableSort.js表格排序插件使用方法详解
Aug 12 Javascript
vue 音乐App QQ音乐搜索列表最新接口跨域设置方法
Sep 25 Javascript
微信小程序实现下拉菜单切换效果
Mar 30 Javascript
基于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
php4与php5的区别小结(配置异同)
2011/12/20 PHP
php在项目中寻找代码的坏味道(综艺命名)
2012/07/19 PHP
PHP使用正则表达式清除超链接文本
2013/11/12 PHP
Codeigniter生成Excel文档的简单方法
2014/06/12 PHP
PHP+jQuery 注册模块的改进(一):验证码存入SESSION
2014/10/14 PHP
利用switch语句进行多选一判断的实例代码
2016/11/14 PHP
JavaScript 组件之旅(二)编码实现和算法
2009/10/28 Javascript
28个JS验证函数收集
2010/03/02 Javascript
用函数模板,写一个简单高效的 JSON 查询器的方法介绍
2013/04/17 Javascript
利用js 进行输入框自动匹配字符的小例子
2013/06/29 Javascript
js验证输入是否为手机号码或电话号码示例
2013/12/30 Javascript
node.js中的querystring.escape方法使用说明
2014/12/10 Javascript
JavaScript中消除闭包的一般方法介绍
2015/03/16 Javascript
异步JS框架的作用以及实现方法
2015/10/29 Javascript
Jquery实现$.fn.extend和$.extend函数
2016/04/14 Javascript
Javascript日期格式化format函数的使用方法
2016/08/30 Javascript
JS数组返回去重后数据的方法解析
2017/01/03 Javascript
EasyUI为Numberbox添加blur事件的方法
2017/03/05 Javascript
node.js+jQuery实现用户登录注册AJAX交互
2017/04/28 jQuery
nodejs基于WS模块实现WebSocket聊天功能的方法
2018/01/12 NodeJs
React中如何引入Angular组件详解
2018/08/09 Javascript
原生JS与CSS实现软件卸载对话框功能
2019/12/05 Javascript
Python使用scrapy采集时伪装成HTTP/1.1的方法
2015/04/08 Python
详解python--模拟轮盘抽奖游戏
2019/04/12 Python
详解如何设置Python环境变量?
2019/05/13 Python
python PIL模块的基本使用
2020/09/29 Python
Python 远程开关机的方法
2020/11/18 Python
python爬取抖音视频的实例分析
2021/01/19 Python
美国受欢迎的女性牛仔裤品牌:DL1961
2016/11/12 全球购物
土地转让协议书
2014/04/15 职场文书
公司领导班子召开党的群众路线教育实践活动总结大会新闻稿
2014/10/21 职场文书
升学宴来宾致辞
2015/07/27 职场文书
宣传部部长竞选稿
2015/11/21 职场文书
大学生党员暑假实践(活动总结)
2019/08/21 职场文书
MySQL中的引号和反引号的区别与用法详解
2021/10/24 MySQL
详解Oracle数据库中自带的所有表结构(sql代码)
2021/11/20 Oracle