Vue2?中的數(shù)據(jù)劫持簡寫示例
package.json 相關依賴
我們今天要編寫的項目通過需要使用 Webpack 進行編譯,package.json 相關依賴如下:
{
"scripts": {
"dev": "webpack-dev-server",
"build:": "webpack"
},
"devDependencies": {
"html-webpack-plugin": "^4.5.2",
"webpack": "^4.46.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.3"
}
}
Webpack.config.js 配置文件
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist")
},
devtool: "source-map",
resolve: {
// 表示解析模塊引入的時候先從當前文件夾尋找模塊,再去 node_modules 找模塊
modules: [
path.resolve(__dirname, ""),
path.resolve(__dirname, "node_modules")
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "public/index.html")
})
]
};
public/index.html 文件內(nèi)容
<!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"></div>
</body>
</html>
全部文件目錄結(jié)構(gòu)

好了,接下來我們就開始發(fā)車!
實例一個模擬的 Vue 應用
首先,我們需要編寫我們的入口文件 index.js,該文件很普通主要就是實例一個模擬的 Vue 應用:
// index.js
// 我們在 webpack.config.js 中進行了配置,所以這里優(yōu)先在當前目錄下尋找 vue 文件,也就是我們的 vue/index.js 文件
import Vue from "vue";
let vm = new Vue({
el: "#app",
data() {
return {
title: "學生列表",
classNum: 1,
teacher: ["張三", "李四"],
info: {
a: {
b: 1
}
},
students: [
{
id: 1,
name: "小紅"
},
{
id: 2,
name: "小明"
}
]
};
}
});
console.log(vm);
vue/index.js 文件主要是負責初始化內(nèi)容
// src/sindex.js
import { initState } from "./init";
function Vue(options) {
this._init(options);
}
Vue.prototype._init = function (options) {
// this 指向當前實例對象
var vm = this;
// 我們把 new Vue() 時候傳遞的數(shù)據(jù)統(tǒng)稱為 options
// 并且掛載到 Vue 的實例對象上
vm.$options = options;
// 調(diào)用 initState 初始化 data 數(shù)據(jù)
initState(vm);
};
export default Vue;
initState方法
vue/init.js 文件暴露出一個initState方法,該方法主要是處理初始化的數(shù)據(jù):
// vue/init.js
import proxyData from "./proxy";
import observer from "./observe"
function initState(vm) {
var options = vm.$options;
// 如果 options 中存在 data 屬性,我們才會繼續(xù)處理
if (options.data) {
initData(vm);
}
}
function initData(vm) {
var data = vm.$options.data;
// 把 data 數(shù)據(jù)單獨保存到 Vue 的實例化對象上,方便我們獲取
// 如果 data 是一個函數(shù),我們需要執(zhí)行返回得到返回的對象
data = vm._data = typeof data === "function" ? data.call(vm) : data || {};
// 遍歷 data 對象,通過 proxyData 對數(shù)據(jù)進行攔截
for (const key in data) {
// 傳入的參數(shù)分別是:當前實例、key值(也就是 vm._data)、data 中的 key 值(例如 vm._data.title)
proxyData(vm, "_data", key);
}
// 調(diào)用觀察者模式
observer(vm._data)
}
export {
initState
};
以上代碼,我們通過proxyData對data中的數(shù)據(jù)進行攔截,詳情如下:
// vue/proxy.js
function proxyData(vm, target, key) {
// 當訪問 vm.title 的時候轉(zhuǎn)換為 vm._data.title
//(請記住這句話?。。。?
Object.defineProperty(vm, key, {
get: function () {
return vm[target][key];
},
set: function (newVal) {
vm[target][key] = newVal;
}
});
}
export default proxyData;
我們還調(diào)用了observer方法進行事件訂閱,詳細如下:
// vue/observe.js
import Observer from "./observer"
function observe(data) {
// 判斷只處理對象,如果不是對象直接返回
if (typeof data !== "object" || data === null) {
return false;
}
// 觀察數(shù)據(jù)
return new Observer(data)
}
export default observe;
核心文件vue/observer.js
接下來就是我們的核心文件vue/observer.js,該文件主要負責對數(shù)據(jù)類型進行判斷,如果是數(shù)組就需要單獨處理數(shù)組,這個我們后面再說:
// vue/observer.js
import defineReactiveData from "./reactive";
import { arrMethods } from "./array";
import observeArr from "./observeArr";
// 這個方法會在多個地方調(diào)用,請記住這個方法以它的作用
function Observer(data) {
// 如果 data 是一個數(shù)組,那面需要單獨處理
if (Array.isArray(data)) {
// 給數(shù)組新增一層原型
data._proto__ = arrMethods;
// 循環(huán)數(shù)組的每一項,然后讓每一項都調(diào)用 Observer 方法進行訂閱
observeArr(data)
} else {
// 處理對象
this.walk(data);
}
}
Observer.prototype.walk = function (data) {
// 獲取到 data 全部的 key
// 也就是我們定義的 ['title', 'classNum', 'teacher', 'info', 'students']
let keys = Object.keys(data);
for (var i = 0; i < keys.length; i++) {
let key = keys[i];
let value = data[key];
// 攔截 data 數(shù)據(jù)
// 分別傳入?yún)?shù)為:vm._data、data 中的 key、data 中 key 對應的 value
defineReactiveData(data, key, value);
}
};
export default Observer;
以上代碼,我們分別對數(shù)組和對象執(zhí)行不同的操作,我們先來看對象的操作:
在Observer構(gòu)造函數(shù)中我們新增了一個walk方法,該方法獲取到了所有的key值,然后調(diào)用了defineReactiveData進行處理。
// vue/reactive.js
import observe from "./observe";
function defineReactiveData(data, key, value) {
// 例如 info.a 還是個對象,那么就遞歸觀察
observe(value);
// 這里的 data 是 vm._data,所以這里攔截的也是 vm._data
Object.defineProperty(data, key, {
get() {
console.log(`?? 響應式獲?。篸ata.${key},`, value);
return value;
},
set(newVal) {
console.log(`?? 響應式設置:data.${key},`, newVal);
if (newVal === value) {
return false;
}
// 如果新值還是對象,那么接著進行觀察
observe(newVal);
value = newVal;
}
});
}
export default defineReactiveData;
以上代碼,我們是對vm._data進行攔截的,這是因為我們前面說的proxyData攔截的是vm對象,當訪問vm.title的時候,proxyData的攔截就會生效,而proxyData內(nèi)部是通過vm._data來獲取的,這樣又會觸發(fā)defineReactiveData的攔截!
vue/observer.js文件對數(shù)組進行處理
回到vue/observer.js文件,我們還需要對數(shù)組進行處理:
import defineReactiveData from "./reactive";
import { arrMethods } from "./array";
import observeArr from "./observeArr";
// 這個方法會在多個地方調(diào)用,請記住這個方法以它的作用
function Observer(data) {
// 如果 data 是一個數(shù)組,那面需要單獨處理
if (Array.isArray(data)) {
// 為數(shù)組更改原型
data._proto__ = arrMethods;
// 循環(huán)數(shù)組的每一項,然后讓每一項都調(diào)用 Observer 方法進行訂閱
observeArr(data)
} else {
// ...
}
}
Observer.prototype.walk = function (data) {
// ...
};
export default Observer;
以上代碼我們對數(shù)組更改一個原型arrMethods,那看看它到底做了什么事情:
// vue/array.js
// ARR_METHODS 是一些可以更改數(shù)組本身的方法,里面包括以下內(nèi)容,我們就不展開看了
// ["push", "pop", "shift", "unshift", "splic", "sort", "reverse"]
import { ARR_METHODS } from "./config";
import observeArr from "./observeArr";
// 把數(shù)組本身的元素進行拷貝
var originArrayMethods = Array.prototype;
// 創(chuàng)建一個空對象,該空對象的原型就是數(shù)組的原型
var arrMethods = Object.create(originArrayMethods);
// 遍歷這些數(shù)組的方法名稱
ARR_METHODS.forEach(function (m) {
// 在新對象上重寫數(shù)組的方法
arrMethods[m] = function () {
// 把數(shù)組接到的參數(shù)轉(zhuǎn)換為一個數(shù)組
var args = Array.prototype.slice.call(arguments);
// 執(zhí)行數(shù)組原本的方法
var rt = originArrayMethods[m].apply(this, args);
var newArr;
switch (m) {
case "push":
case "unshift":
// 例如 arr.push({a: 1})
// args 就是 [{a: 1}]
newArr = args;
break;
case "splice":
// 例如 arr.splice(1, 0, {a: 1}, {b: 2})
// args 就是 [{a: 1}, {b: 2}]
newArr = args.slice(2);
break;
default:
break;
}
// 如果有值那面就調(diào)用 observeArr 方法
// observeArr 方法就是循環(huán)數(shù)組的每一項,然后讓每一項都調(diào)用 Observer 方法進行訂閱
newArr && observeArr(newArr);
return rt;
};
});
export { arrMethods };
以上代碼我們重寫了數(shù)組相關的方法,這是因為這些方法被并不能被Object.defineProperty攔截到。
詳細請看:v-for 列表循環(huán)
所以我們通過重寫方法的方式,讓數(shù)組可以正常的執(zhí)行方法,同時也能被我們的observeArr方法攔截到,所以數(shù)組現(xiàn)在就是這樣多了一層我們寫的原型,但最終它還是繼承于Array構(gòu)造函數(shù)的:

而我們的observeArr只是遍歷了數(shù)組的每一項,讓每一項都進行了攔截:
// vue/observeArr.js
import observe from "./observe";
function observeArr(arr) {
for (let i = 0; i < arr.length; i++) {
// 又回到了起點,進行更新訂閱
observe(arr[i]);
}
}
export default observeArr;
然后我們?nèi)?code>index.js文件獲取屬性,看看結(jié)果:
import Vue from "vue";
let vm = new Vue({
el: "#app",
data() {
return {
title: "學生列表",
classNum: 1,
teacher: ["張三", "李四"],
info: {
a: {
b: 1
}
},
students: [
{
id: 1,
name: "小紅"
},
{
id: 2,
name: "小明"
}
]
};
}
});
console.log(vm);
console.log(vm.title);
console.log(vm.teacher);
console.log(vm.info.a);

以上就是Vue2 中的數(shù)據(jù)劫持簡寫示例的詳細內(nèi)容,更多關于Vue2 數(shù)據(jù)劫持的資料請關注腳本之家其它相關文章!
相關文章
解決el-select數(shù)據(jù)量過大的3種方案
最近做完一個小的后臺管理系統(tǒng),快上線了,發(fā)現(xiàn)一個問題,有2個select的選項框線上的數(shù)據(jù)量是1w+,而測試環(huán)境都是幾百的,所以導致頁面直接卡住了,本文給大家總結(jié)了3種方法,需要的朋友可以參考下2023-09-09
vue+layui實現(xiàn)select動態(tài)加載后臺數(shù)據(jù)的例子
今天小編就為大家分享一篇vue+layui實現(xiàn)select動態(tài)加載后臺數(shù)據(jù)的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09
詳解Vue3?父組件調(diào)用子組件方法($refs?在setup()、<script?setup>?中使用)
這篇文章主要介紹了Vue3?父組件調(diào)用子組件方法($refs?在setup()、<script?setup>?中使用),在 vue2 中 ref 被用來獲取對應的子元素,然后調(diào)用子元素內(nèi)部的方法,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下2022-08-08
vue中$nexttick,$set,$forceupdate的區(qū)別
本文主要介紹了vue中$nexttick,$set,$forceupdate的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-07-07
elementplus實現(xiàn)多級表格(最后一級展示圖片)
本文主要介紹了elementplus實現(xiàn)多級表格(最后一級展示圖片),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-05-05

