文章目录
提示:以下是本篇文章正文内容,下面案例可供参考
一、使用rollup搭建环境
1、安装依赖
具体packsge文件如下,可按照以下安装基础依赖
{
"name": "vuesource",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type":"module",
"scripts": {
"dev": "rollup -cw"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.22.9",
"@babel/preset-env": "^7.22.9",
"rollup": "^3.26.2",
"rollup-plugin-babel": "^4.4.0"
}
}
2、配置rollup.config.js文件
export default{
input:'./src/index.js', //打包入口文件
output:{
file:'dist/vue.js',
format:'umd',//在window上 vue
name:'Vue',
sourcemap:true
}
}
3、配置plugins
1、新建.babelrc文件并写入配置
{
"presets": [
"@babel/preset-env"
]
}
2、配置plugin babel
import babel from 'rollup-plugin-babel'
export default{
input:'./src/index.js', // 打包入口文件(需要新建)
output:{
file:'dist/vue.js', // 打包后自动生成
format:'umd',// 在window上 vue
name:'Vue',
sourcemap:true
},
plugins:[
babel({
// 除了node_modules文件下的第三方依赖不打包
exclude:'node_modules/**'
}),
]
}
二、初始化用户选项
1、编写入口文件
// index.js 入口文件
// 将所有方法都耦合在一起
function Vue( options ){ // options 是用户选项
console.log(options) // 打印 用户选项
}
export default Vue
2、编写初始化函数
// init.js
export function initMixin(Vue){
Vue.prototype._init = function(options){
// vue vm.$options 就是获取用户的配置
// 我们使用 vue的时候 $nextTick $data $attr ....
const vm = this
vm.$options = options //将用户选项挂在到实力上
}
}
3、编写初始化状态函数
// state.js
export function initState(vm){
const opts = vm.$options // 获取用户选项
//初始化 data
if(opts.data){ //判断data是否存在
initData(vm) // 初始化 data 函数
}
//初始化 props
//if(opts.props){
// initprops(vm)
//}
// ...
}
function proxy(vm,target,key){
// 数据劫持 vue2 里采用的一个api defineProperty
Object.defineProproty(vm,key,{
get(){
return vm[target][key]
},
set(newValue){
vm[target][key] = newValue
}
})
}
function initData(vm){
let data = vm.$options.data //在 vue2 中 data 可能是函数或对象
// 当data是函数的时候 调用 data() 要注意 data 的 this 指向是data的实例对象而不是原型
data = typeof data == 'function' ? data.call(vm) : data
vm._data = data //挂载到 vm 用 _data 接收
// 将 vm_data 用 vm 来代理
for(let key in data){
proxy(vm,'_data',key)
}
}
4、编写观测类
// observe/index.js
import { newArrayProto } from "./array";
class Observe{
constructor(data){
// object.defineProperty 只能劫持已经存在的数据,(vue里面回单独写一些api)
// 数组不适合劫持代理,当数组中的原属过多的时候会影响性能
Object.defineProperty(data,'__ob__',{
value:this,
enumerable:false //将 __ob__ 不可枚举
})
// data.__ob__ = this // 给数据加了个标识
if(Array.isArray(data)){
// 重写数组方法,7个数组变异方法可以直接操作数组
data.__proto__ = newArrayProto
this.observeArray(data) // 监控数组当中的 引用类型(对象)
}else{
this.walk(data);
}
}
walk(data){ // 循环对象 对属性依此劫持
// 重新定义属性
Object.keys(data).forEach(key => defineReactive(data,key,data[key]) )
}
observeArray(data){
data.forEach(item => observe(item))
}
}
export function defineReactive(target,key,value){ //闭包
//递归操作
observe(value) //对所有的对象里面的属性进行劫持
Object.defineProperty(target,key,{
get(){ // 取值的时候 会执行 get
return value
},
set(newValue){ // 修改的时候 会执行 set
if(newValue === value) return;
value = newValue
}
})
}
export function observe(data){
// 对这个对象进行劫持
if(typeof data !== 'object' || data ==null){
return; //只对对象进行劫持
}
if(data.__ob__ instanceof Observe){
return data.__ob__
}
//判断对象是否被劫持
return new Observe(data)
}
5、数组部分方法重写
// observe/array.js
// 我们希望重写数组当中的部分方法
let oldArrayProto = Array.prototype // 获取数组当中的原型
export let newArrayProto = Object.create(oldArrayProto)
let methods = [
'push',
'pop',
'shift',
'unshift',
'reverse',
'sort',
'splice'
]
methods.forEach(method => {
newArrayProto[method] = function(...args){ // 重写数组方法
const result = oldArrayProto[method].call(this,...args) // 数组的劫持
// 我们需要对新的值进行劫持
let inserted;
let ob = this.__ob__
switch(method){
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.slice(2)
default:
break;
}
console.log(inserted)
if(inserted){
// 对新增的内容再次进行观测
ob.observeArray(inserted)
}
return result
}
})
6、方法整合
1、在state.js中引入观测类
import { observe } from "./observe/index.js"
// 初始化数据时 应同步观测
function initData(vm){
let data = vm.$options.data // data 可能是函数或对象
data = typeof data === 'function' ? data.call(vm) : data
// console.log(data)
vm._data = data
// 数据劫持 vue2 里采用的一个api defineProperty
observe(data)
// 将 vm_data 用 vm 来代理
for(let key in data){
proxy(vm,'_data',key)
}
}
2、在init.js中引入initState.js
import { initState } from "./state"
export function initMixin(Vue){ //就是给Vue增加init方法
Vue.prototype._init = function(options){ //用于初始化数据
// vue vm.$options 就是获取用户的配置
// 我们使用 vue的时候 $nextTick $data $attr ....
const vm = this
vm.$options = options //将用户选项挂在到实力上
// 初始化状态
initState(vm)
}
}
3、在index.js中引入initMixin 方法
import { initMixin } from "./init"
// 将所有方法都耦合在一起
function Vue( options ){ // options 是用户选项
// debugger
this._init(options)
}
initMixin(Vue) //初始化
export default Vue
三、测试
运行终端 npm run dev
新建 index.html文件
倒入打包文件中的vue.js
<!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>Document</title>
</head>
<body>
</body>
<script src="./dist/vue.js"></script>
<script>
console.log(Vue)
// 响应式数据的变化 数据双向绑定
// 监控数据的取值 和 更改
const vm = new Vue({
//数据代理
data:{
name:'zf',
age:'24',
address:{
num:30,
content:'太平真'
},
arr:['你好','hahah','我得天啊',{a:123}]
}
})
console.log(vm.arr[3].a = 520)
vm.arr.unshift({b:11})
console.log(vm)
</script>
</html>
总结
提示:对aip的引用很多 Object.defineProperty 等