国外教育网站模板,做美食网站的项目背景,wordpress的文章在哪里,公司做网站需要准备什么材料数据驱动 1 #xff09;概述 vue的一个核心思想#xff0c;就是数据驱动 所谓数据驱动#xff0c;就是指视图是由数据驱动生成的 对视图的修改并不会直接操作dom#xff0c;而是通过修改数据 它相比我们传统的前端开发#xff0c;如使用 jQuery 的前端库直接去修改 dom…数据驱动 1 概述 vue的一个核心思想就是数据驱动 所谓数据驱动就是指视图是由数据驱动生成的 对视图的修改并不会直接操作dom而是通过修改数据 它相比我们传统的前端开发如使用 jQuery 的前端库直接去修改 dom 的的话 它大大简化了代码量特别是当交互复杂的时候只关心数据的修改会让代码的逻辑变得非常清晰 因为dom变成了数据的映射我们所有的逻辑都是对数据的修改而并不关注dom这样的代码非常有利于维护 在vue.js中可以采用简洁的模板语法来声明式的将数据渲染为 dom示例 // main.js
import Vue from vuevar app new Vue({el: #app,/*data: {message: Hello},*/data() {return {message: Hello}}
})// App.vue
div idapp{{ message }}
/div这实例的配置有两个参数 el: 是它的一个挂载的dom对象data: 是相关的数据 在模板中就声明是起了这样的一个差值message最终映射到浏览器上可以看到这个文本节点它实际上就生成了一个字符串文本 这个例子是为了说明这个数据在js中定义的这个数据 最终是怎么渲染到dom上的 这就是 new Vue 的时候帮我们做的这些事情
2 数据驱动的关注
第一分析数据是怎么映射到dom的 传入了这样一个javascript对象最终怎么生成到dom上的 第二数据的变化驱动视图的变化 对message的修改视图是怎么跟着变化的
new Vue时做了哪些处理
在 src/core/instance/index.js 中function Vue (options) {if (process.env.NODE_ENV ! production !(this instanceof Vue)) {warn(Vue is a constructor and should be called with the new keyword)}this._init(options)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)export default Vue它实际上就是一个class也就是一个function实现的这个class它实际上就是执行了这个原型上的 _init 方法这个 _init 方法在 src/core/instance/init.js 中定义注意这个 _init 方法是执行下面的 initMixin(Vue) 时才被挂载的就在这个 initMixin 方法的内部挂载了 _init, 如下Vue.prototype._init function (options?: Object) {const vm: Component this// a uidvm._uid uidlet startTag, endTag/* istanbul ignore if */if (process.env.NODE_ENV ! production config.performance mark) {startTag vue-perf-start:${vm._uid}endTag vue-perf-end:${vm._uid}mark(startTag)}// a flag to avoid this being observedvm._isVue true// merge optionsif (options options._isComponent) {// optimize internal component instantiation// since dynamic options merging is pretty slow, and none of the// internal component options needs special treatment.initInternalComponent(vm, options)} else {vm.$options mergeOptions(resolveConstructorOptions(vm.constructor),options || {},vm)}/* istanbul ignore else */if (process.env.NODE_ENV ! production) {initProxy(vm)} else {vm._renderProxy vm}// expose real selfvm._self vminitLifecycle(vm)initEvents(vm)initRender(vm)callHook(vm, beforeCreate)initInjections(vm) // resolve injections before data/propsinitState(vm)initProvide(vm) // resolve provide after data/propscallHook(vm, created)/* istanbul ignore if */if (process.env.NODE_ENV ! production config.performance mark) {vm._name formatComponentName(vm, false)mark(endTag)measure(vue ${vm._name} init, startTag, endTag)}if (vm.$options.el) {vm.$mount(vm.$options.el)}
}在这个 _init 方法中做了一堆初始化的工作 比如定义 uid合并options 它会把传入的options最终都merge到 $options 上可以通过 $options.el 访问到我们初始化时的 el 对象可以通过 $options.data 访问到我们定义的 data 后面就是一堆初始化函数 比如初始化生命周期事件中心renderinjections, state, provide, state 这些注意中间的两次 callHooks 的调用 最后挂载这个 el 对象 在上面的demo里面, 定义了这个data需要看下这个data怎么初始化的var app new Vue({el: #app,/*data: {message: Hello},*/data() {return {message: Hello}}mounted() {console.log(this.message); // Helloconsole.log(this._data.message); // Hello}
})现在看下为何能够通过 this.message 访问到数据的进入上述的 initState, 定义在 src/core/instance/state.jsconst sharedPropertyDefinition {enumerable: true,configurable: true,get: noop,set: noop
}export function proxy (target: Object, sourceKey: string, key: string) {sharedPropertyDefinition.get function proxyGetter () {return this[sourceKey][key]}sharedPropertyDefinition.set function proxySetter (val) {this[sourceKey][key] val}Object.defineProperty(target, key, sharedPropertyDefinition)
}export function initState (vm: Component) {vm._watchers []const opts vm.$optionsif (opts.props) initProps(vm, opts.props)if (opts.methods) initMethods(vm, opts.methods)if (opts.data) {initData(vm)} else {observe(vm._data {}, true /* asRootData */)}if (opts.computed) initComputed(vm, opts.computed)if (opts.watch opts.watch ! nativeWatch) {initWatch(vm, opts.watch)}
}function initData (vm: Component) {let data vm.$options.datadata vm._data typeof data function? getData(data, vm): data || {}if (!isPlainObject(data)) {data {}process.env.NODE_ENV ! production warn(data functions should return an object:\n https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function,vm)}// proxy data on instanceconst keys Object.keys(data)const props vm.$options.propsconst methods vm.$options.methodslet i keys.lengthwhile (i--) {const key keys[i]if (process.env.NODE_ENV ! production) {if (methods hasOwn(methods, key)) {warn(Method ${key} has already been defined as a data property.,vm)}}if (props hasOwn(props, key)) {process.env.NODE_ENV ! production warn(The data property ${key} is already declared as a prop. Use prop default value instead.,vm)} else if (!isReserved(key)) {proxy(vm, _data, key)}}// observe dataobserve(data, true /* asRootData */)
}export function getData (data: Function, vm: Component): any {// #7573 disable dep collection when invoking data getterspushTarget()try {return data.call(vm, vm)} catch (e) {handleError(e, vm, data())return {}} finally {popTarget()}
}在 initState 中 如果有 props, 则初始化 props如果有 methods, 则初始化 methods如果有 data, 则初始化 data看 initData 在 initData 中 从 $options 中拿到 data判断 data 是否是一个 function, 通常我们推荐写一个function, 而非直接使用一个对象如果是函数则调用 getData, 否则直接使用 data后面处理好的 data 不是对象在 dev 环境中进行输出警告再后面 拿到 keys, props, methods它们之间就做了一个循环对比 比如说在这个 data 里面定义了这个message那就不能在 props 下也用 message或者在 methods 里面用这个 message为什么不能用为什么会冲突是因为它们最终都会挂载到 vm 上也就是说, 这个message它最终会挂到这个this这样this.message 就可以访问到当前数据了 那这个是怎么实现的呢实际上就通过这个 proxy 函数实现的proxy 顾名思义就是代理 它实际上通过这个 sharedPropertyDefinition 对象定义了一个get 和一个set 两个函数然后通过 Object.defineProperty这个方法代理了这个 target 的 key就是对 target 的 key 的访问做了一层 get 和 set这个 target 实际上就是 vm也就是说访问 vm[key], 这个 getter 函数就会执行返回 this[sourceKey][key]这个 sourceKey 就是 _data, 也就是说当我们访问 this.message 实际上是从 this._data.message 中获取的 因为它是通过这个 proxy 做层代理在调用 proxy 的时候实际上就是把这个 _data 作为 sourceKey 传入这是通过 this.message 能狗访问到 message 定义到的数据的原因但要注意_data在后续开发中不要使用这个_表示私有不对外提供类似的props的访问也类似这里暂时跳过之后通过调用 observe 对 data 做一个响应式处理, 这块也跳过 总结 当执行 new Vue 的时候它实际上是执行了 _init 的方法这个方法做一堆初始化的工作先是对 options做合并接下来就执行一系列的方法在其中的 initState 过程中对 data 做一层 proxy 的处理最后对data做响应式处理最后会调用 vm.$mount 将 el 进行挂载
扩展 调试Vue2源码小技巧
一般而言我们都在 dev 环境下调试源码的没有说在生产环境下调试我们如果需要调试哪个API或相关流程需要先建一个小demo项目之后在当前项目的 node_modules 中找到 vue 目录查看 package.json 找到 module 配置dist/vue.runtime.esm.js其实这里并不是真实的入口 这个demo实际上是 vue-cli 生成的这个模板工程是由webpack构建出来的在build/webpack.base.conf.js 中的resolve 属性中配置了 alias{vue: resolve(node_modules/vue/dist/vue.esm.js),: resolve(src),
}所以真实的源码是在 node_modules/vue/dist/vue.esm.js 中这个在 vue-cli 初始化工程的时候如果选择了 Runtime Compiler 版本时会添加这个 alias 的 vue 这行代码所以真正使用的是这个文件这个是一个大的打包后的代码可以在这个文件中搜索方法并进行 debugger后续如何调试就忽略不再赘述了