首先使用npm init -y
建立初始化的配置文件,而後下載一下咱們後面須要的開發依賴:javascript
npm i webpack webpack-cli webpack-dev-server html-webpack-plugin --save-dev
html
新建 webpack.config.js
用來編寫webpack
配置, 新建src/index.js
作爲項目入口文件,而後新建public/index.html
作爲模板文件。webpack
基本配置內容以下:vue
const path = require('path'); const htmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', // 以咱們src 的index.js爲入口進行打包 output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, devtool: 'source-map', resolve: { // 默認讓import去咱們的source文件夾去查找模塊,其次去node_modules查找 modules: [path.resolve(__dirname, 'source'), path.resolve('node_modules')] }, plugins: [ new htmlWebpackPlugin({ template: path.resolve(__dirname, 'public/index.html') }) ] }
使用npm scripts
啓動咱們的項目, 將 package.json
裏面的scripts
修改成:java
"scripts": { "start": "webpack-dev-server", "build": "webpack" }
而後執行npm start
可啓動咱們的項目了。node
上面修改了咱們的默認引入文件路徑,此時咱們在source
文件夾下新建vue/index.js
,而後在咱們的src/index.js
中引入webpack
import Vue from 'vue'; // 會默認查找 source 目錄下的 vue 文件夾
此時在source/vue/index.js
的js
就能夠正常執行了。git
下面繼續編寫咱們的src/index.js
,前面已經引入了咱們本身編寫的Vue
,後面咱們模擬Vue
的語法去編寫:github
import Vue from 'vue'; // 會默認查找 source 目錄下的 vue 文件夾 let vm = new Vue({ el: '#app', // 表示要渲染的元素是#app data() { return { msg: 'hello', school: { name: 'black', age: 18 }, arr: [1, 2, 3], } }, computed: { }, watch: { } });
接着咱們就要去編寫實現咱們 Vue
代碼了, vue/index.js
:web
import {initState} from './observe'; function Vue(options) { // Vue 中原始用戶傳入的數據 this._init(options); // 初始化 Vue, 而且將用戶選項傳入 } Vue.prototype._init = function(options) { // vue 中的初始化 this.$options 表示 Vue 中的參數 let vm = this; vm.$options = options; // MVVM 原理, 須要數據從新初始化 initState(vm); } export default Vue; // 首先默認導出一個Vue
首先咱們須要去初始化Vue
,將用戶傳入的數據進行初始化處理,新建observe/index.js
去實現用戶傳入數據的初始化:npm
export function initState(vm) { let opts = vm.$options; if (opts.data) { initData(vm); // 初始化數據 } if (opts.computed) { initComputed(); // 初始化計算屬性 } if (opts.watch) { initWatch(); // 初始化 watch } } /** * 初始化數據 * 將用戶傳入的數據 經過Object.defineProperty從新定義 */ function initData(vm) { } /** * 初始化計算屬性 */ function initComputed() { } /** * 初始化watch */ function initWatch() { }
首先咱們去實現用戶傳入的data
的初始化,也就是常常提到的使用Object.defineProperty
進行數據劫持,重寫getter
和setter
,下面去實現 initData
的具體內容:
/** * 將對vm上的取值、賦值操做代理到 vm._data 屬性上 * 代理數據 實現 例如:vm.msg = vm._data.msg */ function proxy(vm, source, key) { Object.defineProperty(vm, key, { get() { return vm[source][key]; }, set(newValue) { vm[source][key] = newValue; } }) } /** * 初始化數據 * 將用戶傳入的數據 經過Object.defineProperty從新定義 */ function initData(vm) { let data = vm.$options.data; // 用戶傳入的data data = vm._data = typeof data === 'function' ? data.call(vm) : data || {}; for(let key in data) { proxy(vm, '_data', key); // 將對vm上的取值、賦值操做代理到 vm._data 屬性上,便於咱們直接使用vm取值 } observe(vm._data); // 觀察數據 } export function observe(data) { if(typeof data !== 'object' || data == null) { return; // 不是對象或爲null 不執行後續邏輯 } return new Observer(data); }
咱們去該目錄下新建observer.js
去實現咱們的Observer
:
import {observe} from './index'; /** * 定義響應式的數據變化 * @param {Object} data 用戶傳入的data * @param {string} key data的key * @param {*} value data對應key的value */ export function defineReactive(data, key, value) { observe(value); // 若是value依舊是一個對象,須要深度劫持 Object.defineProperty(data, key, { get() { console.log('獲取數據'); return value; }, set(newValue) { console.log('設置數據'); if (newValue === value) return; value = newValue; } }); } class Observer { constructor(data) { // data === vm._data // 將用戶的數據使用 Object.defineProperty從新定義 this.walk(data); } /** * 循環數據遍歷 */ walk(data) { let keys = Object.keys(data); for(let i = 0; i < keys.length; i++) { let key = keys[i]; let value = data[key]; defineReactive(data, key, value); } } } export default Observer;
到這咱們就初步實現了對用戶傳入data
的數據劫持,看一下效果。
此時若是咱們在src/index.js
去取和修改data
中的值:
console.log(vm.msg); console.log(vm.msg = 'world');
到如今爲止咱們就實現了第一部分,對用戶數據傳入的data
進行了數據劫持,可是若是咱們使用vm.arr.push(123)
,會發現只有獲取數據而沒有設置數據,這也就是Object.defineProperty
的弊端,沒有實現對數組的劫持,下一部分去實現一下數組的數據劫持。
代碼部分可看本次提交commit
但願各位大佬點個star,小弟跪謝~