本次主要跟大家分享学习下 vue2
的部分知识。网上大部分知识讲解都是抽离的形式,对应哪个函数直接拿出来讲解,但是对于新手或者不了解内在原理的,逻辑图谱不是很清晰,不成体系。很容易忘记,所以本次系列计划从零还是手写,实现部分功能。
目前大部分公司还是使用的 2 版本,vue3.2
发布后,还不跟 3.0 兼容。求职应聘也主要是 2.0 为主,所以本次主要学习下 vue2
中的一些功能(后面有机会在整理下 vue3
的实现),例如:响应式实现、模板编译解析、依赖收集等功能的实现,尽量保证大家跟着写可以实现自己的版本,尽量把要点写清,有不对的欢迎指正,没懂的欢迎交流。面后会发布到 github
,交流学习。
我们选用 rollup
进行项目启动,这里为什么不用 webpack
呢?
rollup
属于库开发用的,打包体积小,专注打包js
。webpack
当然也是可以的,但是它更适于应用类的,项目类的打包,需要处理img
、css
、font
等,而且打包体积也比较大。
我们运行按照自己配置的形式执行
import babel from 'rollup-plugin-babel'
export default {
// 入口文件
input: './src/index.js',
// 输出配置
output: {
// 输出的文件位置
file: 'dist/vue.js',
format: 'umd', // 打包规范,通用打包格式,包含 commonjs ,给node用的;amd 基本没用过;global 形式,声明变量到 window 上
name: 'Vue', // window.Vue
sourcemap: true
},
plugins: [
babel({
exclude: 'node_modules/**' // 打包排除第三方包
})
]
}
'start': 'rollup -cw' // w 监听文件改变, c 使用配置文件
写几行测试代码,执行命令 npm run start
,我们测试一下
/// src/index.js
let a = 123
function Vue(options) {
}
console.log(a, Vue)
export default Vue // 一定要导出
我们写个 html
文件,引入打包后的 js
/// example/example.html
....
<script src="../dist/vue.js"></script>
......
console.log(Vue) // 可以在浏览器中查看
小节:到这里我们的项目就搭建完成了,主要是配置
rollup
打包,能够在 html 我呢间中访问
// src/index.js
/**
当我们 new Vue 时,其实就是初始化我们写的选项,data、props 等
*/
function Vue() {
// 具体的初始化内容
this._init()
}
export default Vue
因为这里方便我们在原型链上拓展,调用直接
this.xxx
,如果写成类了,更多的是使用继承,需要维护比不方便;虽然类本质也是函数,但是很少见用了类再写个原型的,不规范;我们平时拓展也是Vue.prototype.xxx = xxx
,在全局都可以使用。
因为我们使用函数定义,所以调用原型方法都可以使用 this.xx
,但是我们不好每个文件都引入 Vue
函数,去在原型挂载。所以这里我们导出初始化函数给 index.js
,把 Vue
传进去,方便维护
//// src/init.js
// 这样我们只需要在 src/index.js 中维护 Vue,
export function initMixin(Vue) {
Vue.prototype._init = function(options) { // new Vue 参数
console.log(options)
}
}
//// example.html 文件中测试
const vm = new Vue({
data: {
a: 1,
b: 2
}
})
我们在 init
中主要做两件事,一个是初始化数据,data
、props
、methods
等,一个是挂载到 el
上
//// _init 函数
// 使用 vm ,避免都叫 this,多了分不清
const vm = this
// 初始化属性
initState(vm)
if(options.el) {
// 有 el 执行挂载
vm.$mount(options.el)
}
//// src/state.js
export function initState(vm) {
const options = vm.$options
if (options.data) {
initData(vm)
}
if (options.computed) {
initComputed(vm)
}
if (options.watch) {
initWatch(vm)
}
}
function initData(vm) {}
// 后面去实现
function initComputed(vm) {}
function initWatch(vm) {}
//// init.js
Vue.prototype.$mount = function (el) {
el = document.querySelector(el)
const vm = this
vm.$el = el
// 后面添加模板解析
}
目前得到的 vm
结构
vm
数据指向 data
我们平时定义数据都是 this.xxx,访问的 data 中的值,实际上使用的代理
function initData(vm) {
let data = vm.$options.data // 用户传的 data
// 如果传的是 函数,执行 _data 标识内部属性
data = vm._data = typeof data === 'function' ? data.call(vm) : data
for(let key in data) {
proxy(vm, '_data', key)
}
// 下一篇实现数据响应式**
observe(data)
}
function proxy(target, key, property) {
Object.defineProperty(target, property, {
get() {
return target[key][property]
},
set(v) {
target[key][property] = v
}
})
}
其实如果保证组件使用一次,可以不使用函数。但是如果定义的组件被使用多次,如果
data
是对象的话,引用类型,就会相互影响。如果是函数的话,每次执行返回新的对象,不会冲突
本篇介绍了项目搭建和数据的初始化,我们下一篇去实现 vue2
响应式数据的实现,如何递归,怎么处理数组的。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。